Permettez-moi de vous donner le contexte.
Nous avons une application (de taille moyenne) qui utilise MessageBox.Show (....) à divers endroits (par centaines).
Ces boîtes de message font partie du flux de travail et sont utilisées pour informer, avertir ou recevoir les commentaires d'un utilisateur. L'application est censée se déconnecter automatiquement après un certain temps s'il n'y a pas d'activité. Nous avons besoin que lors de la déconnexion de l'application, juste pour nettoyer les données de session, pour effacer les vues et pour se cacher afin qu'au prochain lancement, il n'ait pas à exécuter le processus de démarrage qui est coûteux en temps.
Tout fonctionne bien mais dans un scénario où il y a une boîte de message à l'écran et que l'utilisateur a quitté la machine sans répondre à la boîte de message, puis en raison d'aucune activité pour obliger l'application à se déconnecter. Le problème est que la boîte de message ne disparaîtra pas.
Comment puis-je fermer la boîte de message ouverte, le cas échéant, tout en masquant l'application?
Voici un morceau de code basé sur IAutomation (une API cool mais pas encore très utilisée) qui tente de fermer toutes les fenêtres modales (y compris celle ouverte avec MessageBox) du processus actuel:
/// <summary>
/// Attempt to close modal windows if there are any.
/// </summary>
public static void CloseModalWindows()
{
// get the main window
AutomationElement root = AutomationElement.FromHandle(Process.GetCurrentProcess().MainWindowHandle);
if (root == null)
return;
// it should implement the Window pattern
object pattern;
if (!root.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
return;
WindowPattern window = (WindowPattern)pattern;
if (window.Current.WindowInteractionState != WindowInteractionState.ReadyForUserInteraction)
{
// get sub windows
foreach (AutomationElement element in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
{
// hmmm... is it really a window?
if (element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
{
// if it's ready, try to close it
WindowPattern childWindow = (WindowPattern)pattern;
if (childWindow.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction)
{
childWindow.Close();
}
}
}
}
}
Par exemple, si vous avez une application WinForms qui affiche une MessageBox lorsque vous appuyez sur un bouton1, vous pourrez toujours fermer l'application en utilisant le menu "Fermer la fenêtre" de Windows (clic droit dans la barre des tâches):
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Don't click me. I want to be closed automatically!");
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_CLOSE = 0xF060;
if (m.Msg == WM_SYSCOMMAND) // this is sent even if a modal MessageBox is shown
{
if ((int)m.WParam == SC_CLOSE)
{
CloseModalWindows();
Close();
}
}
base.WndProc(ref m);
}
Vous pouvez utiliser CloseModalWindows ailleurs dans votre code, bien sûr, ce n'est qu'un exemple.
Cette lien sur les forums MSDN montre comment fermer une boîte de message en utilisant FindWindow
et en envoyant un WM_CLOSE
message. Bien que la question ait été posée pour .NET/WindowsCE, cela pourrait résoudre votre problème, ça vaut le coup
D'abord une question: si des boîtes de messages sont utilisées dans le cadre du flux de travail, la fermeture par programme de la boîte de message ne fera-t-elle pas changer/continuer le flux?
Je pense que vous avez trois options
Créez votre propre version de la classe messagebox qui ouvre une fenêtre de dialogue qui ressemble à une boîte de message avec des fonctionnalités supplémentaires afin qu'elle se ferme automatiquement après un certain temps.
Implémentez quelque chose comme ça en c # pour fermer les boîtes de message par programme. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx
Débarrassez-vous des boîtes de message d'interrompre le flux de travail. C'est probablement la meilleure solution, car le fait de fermer une boîte de message par programme entraînera la poursuite/modification du flux de travail, et peut-être même une autre boîte de message à afficher, ce qui peut ne pas être souhaitable. Évidemment, résoudre le problème racine peut être le meilleur, mais ce n'est pas toujours le plus simple.
1 et 2 devraient être effectués à partir d'un thread séparé, vous devrez donc réfléchir aux implications de cela car l'affichage de la boîte de message sera bloquant.
Voici mon exemple avec SendKeys - testé et fonctionnel:
disons que nous avons un backgroundworker et un bouton sous forme. Après avoir cliqué sur le bouton - démarrer le travailleur et afficher la boîte de message. Chez les travailleurs, l'événement DoWork se met en veille pendant 5 secondes, puis envoie la touche Entrée - la boîte de message est fermée.
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
MessageBox.Show("Close this message!");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(5000);
SendKeys.SendWait("{Enter}");//or Esc
}
Reportez-vous à Message DmitryG dans "Fermer un MessageBox après plusieurs secondes"
Fermeture automatique de MessageBox après la fin du délai d'attente
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
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);
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(null, _caption);
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);
}
et appelez-le via
AutoClosingMessageBox.Show("Content", "Title", TimeOut);
J'ai utilisé .net 2 et deux approches avec la même astuce.
Ouvrez le MessageBox depuis stub-Form avec MessageBox.Show(this,"message")
Lorsque le formulaire n'est pas visible ou n'a pas vraiment d'interface utilisateur.
Gardez le gestionnaire de formulaires et fermez-le avec:
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
ou
en maintenant le formulaire comme paramètre de classe et en utilisant FormX.Close()
.
Étant donné que le formulaire est le propriétaire de la MessageBox, sa fermeture fermera la MessageBox.
Prenant comme hypothèse que vous pouvez éditer le code qui appelle la méthode MessageBox.Show()
, je recommanderais de ne pas utiliser MessageBox. À la place, utilisez simplement votre propre formulaire personnalisé en appelant ShowDialog()
dessus pour faire essentiellement la même chose que la classe MessageBox. Ensuite, vous avez l'instance du formulaire lui-même et vous pouvez appeler Close()
sur cette instance pour la fermer.
Un bon exemple est ici .
Je pense que le moyen le plus propre serait de mettre en œuvre votre propre formulaire de boîte de message comme
class MyMessageBox : Form {
private MyMessageBox currentForm; // The currently active message box
public static Show(....) { // same as MessageBox.Show
// ...
}
public static Show(...) { // define additional overloads
}
public static CloseCurrent() {
if (currentForm != null)
currentForm.Close();
}
// ...
}
Dans certains de mes projets plus importants, j'ai trouvé cette approche utile également à d'autres fins (comme la journalisation automatique des messages d'erreur, etc.)
La deuxième idée que j'aurais serait d'utiliser GetTopWindow()
(ou peut-être une autre fonction WIN32) pour obtenir la fenêtre de niveau supérieur actuelle de votre application et lui envoyer un message WM_CLOSE
.
Ce sujet a été abondamment couvert dans d'autres SO questions, mais comme celui-ci contient plusieurs réponses sur l'utilisation des techniques d'automatisation d'interface utilisateur/de recherche de fenêtres (que je n'aime pas particulièrement) et des suggestions génériques sur la création de propres sans code fourni, j'ai décidé de poster ma propre solution. On peut créer une classe de type MessageBox
instanciable comme suit:
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace Common
{
// Loosely based on: https://www.codeproject.com/Articles/17253/A-Custom-Message-Box
class MsgBox : Form
{
private Panel _plHeader = new Panel();
private Panel _plFooter = new Panel();
private Panel _plIcon = new Panel();
private PictureBox _picIcon = new PictureBox();
private FlowLayoutPanel _flpButtons = new FlowLayoutPanel();
private Label _lblMessage;
private MsgBox()
{
FormBorderStyle = FormBorderStyle.FixedDialog;
BackColor = Color.White;
StartPosition = FormStartPosition.CenterScreen;
MinimizeBox = false;
MaximizeBox = false;
ShowIcon = false;
Width = 400;
_lblMessage = new Label();
_lblMessage.Font = new Font("Segoe UI", 10);
_lblMessage.Dock = DockStyle.Fill;
_lblMessage.TextAlign = ContentAlignment.MiddleLeft;
_flpButtons.FlowDirection = FlowDirection.RightToLeft;
_flpButtons.Dock = DockStyle.Fill;
//_plHeader.FlowDirection = FlowDirection.TopDown;
_plHeader.Dock = DockStyle.Fill;
_plHeader.Padding = new Padding(20);
_plHeader.Controls.Add(_lblMessage);
_plFooter.Dock = DockStyle.Bottom;
_plFooter.BackColor = Color.FromArgb(240, 240, 240);
_plFooter.Padding = new Padding(10);
_plFooter.Height = 60;
_plFooter.Controls.Add(_flpButtons);
_picIcon.Location = new Point(30, 50);
_plIcon.Dock = DockStyle.Left;
_plIcon.Padding = new Padding(20);
_plIcon.Width = 70;
_plIcon.Controls.Add(_picIcon);
Controls.Add(_plHeader);
Controls.Add(_plIcon);
Controls.Add(_plFooter);
}
public static DialogResult Show(IWin32Window owner, string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = Create(message, title, buttons, icon);
return msgBox.ShowDialog(owner);
}
public static DialogResult Show(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = Create(message, title, buttons, icon);
return msgBox.ShowDialog();
}
public static MsgBox Create(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = new MsgBox();
msgBox.Init(message, title, buttons, icon);
return msgBox;
}
void Init(string message, string title, MessageBoxButtons? buttons, MessageBoxIcon icon)
{
_lblMessage.Text = message;
Text = title;
InitButtons(buttons);
InitIcon(icon);
Size = MessageSize(message);
}
void InitButtons(MessageBoxButtons? buttons)
{
if (!buttons.HasValue)
return;
switch (buttons)
{
case MessageBoxButtons.AbortRetryIgnore:
AddButton("Ignore");
AddButton("Retry");
AddButton("Abort");
break;
case MessageBoxButtons.OK:
AddButton("OK");
break;
case MessageBoxButtons.OKCancel:
AddButton("Cancel");
AddButton("OK");
break;
case MessageBoxButtons.RetryCancel:
AddButton("Cancel");
AddButton("Retry");
break;
case MessageBoxButtons.YesNo:
AddButton("No");
AddButton("Yes");
break;
case MessageBoxButtons.YesNoCancel:
AddButton("Cancel");
AddButton("No");
AddButton("Yes");
break;
}
}
void InitIcon(MessageBoxIcon icon)
{
switch (icon)
{
case MessageBoxIcon.None:
_picIcon.Hide();
break;
case MessageBoxIcon.Exclamation:
_picIcon.Image = SystemIcons.Exclamation.ToBitmap();
break;
case MessageBoxIcon.Error:
_picIcon.Image = SystemIcons.Error.ToBitmap();
break;
case MessageBoxIcon.Information:
_picIcon.Image = SystemIcons.Information.ToBitmap();
break;
case MessageBoxIcon.Question:
_picIcon.Image = SystemIcons.Question.ToBitmap();
break;
}
_picIcon.Width = _picIcon.Image.Width;
_picIcon.Height = _picIcon.Image.Height;
}
private void ButtonClick(object sender, EventArgs e)
{
Button btn = (Button)sender;
switch (btn.Text)
{
case "Abort":
DialogResult = DialogResult.Abort;
break;
case "Retry":
DialogResult = DialogResult.Retry;
break;
case "Ignore":
DialogResult = DialogResult.Ignore;
break;
case "OK":
DialogResult = DialogResult.OK;
break;
case "Cancel":
DialogResult = DialogResult.Cancel;
break;
case "Yes":
DialogResult = DialogResult.Yes;
break;
case "No":
DialogResult = DialogResult.No;
break;
}
Close();
}
private static Size MessageSize(string message)
{
int width=350;
int height = 230;
SizeF size = TextRenderer.MeasureText(message, new Font("Segoe UI", 10));
if (message.Length < 150)
{
if ((int)size.Width > 350)
{
width = (int)size.Width;
}
}
else
{
string[] groups = (from Match m in Regex.Matches(message, ".{1,180}") select m.Value).ToArray();
int lines = groups.Length+1;
width = 700;
height += (int)(size.Height+10) * lines;
}
return new Size(width, height);
}
private void AddButton(string caption)
{
var btn = new Button();
btn.Text = caption;
btn.Font = new Font("Segoe UI", 8);
btn.BackColor = Color.FromArgb(225, 225, 225);
btn.Padding = new Padding(3);
btn.Height = 30;
btn.Click += ButtonClick;
_flpButtons.Controls.Add(btn);
}
}
}
On peut alors simplement conserver la référence de la boîte de dialogue dans une portée de classe, afficher la boîte de dialogue et obtenir le résultat, ou simplement la fermer dans un gestionnaire d'événements de sortie d'application.
MsgBox _msgBox;
void eventHandler1(object sender, EventArgs e)
{
_msgBox = MsgBox.Create("Do you want to continue", "Inquiry", MessageBoxButtons.YesNo);
var result = _msgBox.ShowDialog();
// do something with result
}
void applicationExitHandler(object sender, EventArgs e)
{
if (_msgBox != null)
_msgBox.Close();
}
La solution la plus simple consiste à créer un formulaire qui se fermera sur timer_tick
private int interval = 0;
private string message = "";
public msgBox(string msg = "", int i = 0)
{
InitializeComponent();
interval = i;
message = msg;
}
private void MsgBox_Load(object sender, EventArgs e)
{
if (interval > 0)
timer1.Interval = interval;
lblMessage.Text = message;
lblMessage.Width = panel1.Width - 20;
lblMessage.Left = 10;
}
private void Timer1_Tick(object sender, EventArgs e)
{
this.Close();
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
Méthode à utiliser sous forme principale
private void showMessage(string msg, int interval = 0)
{
msgBox mb = new msgBox(msg, interval);
mb.ShowDialog(this);
}
Appeler
showMessage("File saved");
Créez votre propre contrôle pour cela et implémentez le comportement que vous souhaitez y avoir. En option, il peut y avoir un temporisateur pour fermer ce MessageBox.