背景を説明しましょう。
さまざまな場所(数百)でMessageBox.Show(....)を使用しているアプリケーション(中サイズ)があります。
これらのメッセージボックスはワークフローの一部であり、ユーザーからの通知、警告、または入力を取るために使用されます。アクティビティがない場合、アプリケーションは一定時間後に自動的にログオフすることになっています。アプリケーションをログアウトするときは、セッションデータをクリーンアップし、ビューをクリアし、それ自体を非表示にして、次回の起動時に時間のかかる起動プロセスを実行する必要がないようにする必要があります。
すべてが正常に機能していますが、画面上にメッセージボックスがあり、ユーザーがメッセージボックスに応答せずにマシンを離れた場合、およびアプリケーションにログアウトさせるアクティビティがないために、シナリオで動作します。問題はメッセージボックスが消えないことです。
アプリケーションを非表示にして、開いているメッセージボックスがある場合、それを閉じるにはどうすればよいですか?
以下は、現在のプロセスのすべてのモーダルウィンドウ(MessageBoxで開かれたものを含む)を閉じようとする IAutomation (かっこいいがまだあまり使用されていないAPI)に基づくコードです。
/// <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();
}
}
}
}
}
たとえば、ボタン1を押すとメッセージボックスがポップアップするWinFormsアプリケーションがある場合は、Windowsの[ウィンドウを閉じる]メニュー(タスクバーで右クリック)を使用してアプリを閉じることができます。
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);
}
もちろん、コードのどこかでCloseModalWindowsを使用できます。これは単なるサンプルです。
これは、MSDNフォーラムの link で、FindWindow
を使用してWM_CLOSE
メッセージを送信し、メッセージボックスを閉じる方法を示しています。質問は.NET/WindowsCEに対して行われましたが、問題を解決する可能性があります。
最初の質問:メッセージボックスがワークフローの一部として使用されている場合、プログラムでメッセージボックスを閉じるとフローが変更/続行されませんか?
3つのオプションがあると思います
独自のバージョンのメッセージボックスクラスを作成して、一定期間後に自動的に閉じる機能が追加されたメッセージボックスのようなダイアログウィンドウを開きます。
プログラムでメッセージボックスを閉じるには、c#でこのようなものを実装します。 http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx
ワークフローの中断からメッセージボックスを削除します。メッセージボックスをプログラムで閉じると、ワークフローが続行/変更され、望ましくない可能性がある別のメッセージボックスが表示されることもあるため、これはおそらく最良の解決策です。しかし、根本的な問題を修正するのが最善の方法かもしれませんが、常に最も簡単であるとは限りません。
1と2は別のスレッドから実行する必要があるため、メッセージボックスを表示するとブロックされるので、その影響を考慮する必要があります。
SendKeysを使用した私の例-テスト済みで動作します。
フォームにbackgroundworkerとボタンがあるとしましょう。ボタンがクリックされた後-ワーカーを起動してメッセージボックスを表示します。ワーカーでは、DoWorkイベントが5秒間スリープし、Enterキーを送信します-メッセージボックスが閉じています。
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
}
「数秒後にメッセージボックスを閉じる」の DmitryGの投稿を参照してください
タイムアウトに達した後のメッセージボックスの自動クローズ
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);
}
そしてそれを介してそれを呼び出します
AutoClosingMessageBox.Show("Content", "Title", TimeOut);
私は同じトリックで.net 2と2つのアプローチを使用しました。
MessageBox.Show(this,"message")
を使用して、スタブフォームからメッセージボックスを開きます。
フォームが表示されない、または実際にUIがない場合。
フォームハンドラーを保持し、次のコマンドで閉じます。
_static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
_
または
フォームをクラスパラメータとして保持し、FormX.Close()
を使用します。
フォームはメッセージボックスの所有者なので、閉じるとメッセージボックスが閉じます。
MessageBox.Show()
メソッドを呼び出すコードを編集できることを前提として、MessageBoxを使用しないことをお勧めします。代わりに、独自のカスタムフォームを使用してShowDialog()
を呼び出し、基本的にMessageBoxクラスと同じことを行います。次に、フォーム自体のインスタンスがあり、そのインスタンスでClose()
を呼び出してフォームを閉じることができます。
良い例が here です。
私はあなたのようなメッセージボックスフォームを実装するのが最もきれいな方法だと思います
_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();
}
// ...
}
_
大規模なプロジェクトのいくつかでは、この方法が他の目的(エラーメッセージの自動ログ記録など)にも役立つことがわかりました。
2番目のアイデアは、GetTopWindow()
(または他のWIN32関数)を使用して、アプリケーションの現在の最上位ウィンドウを取得し、_WM_CLOSE
_メッセージを送信することです。
このトピックは、他のSO=質問で豊富にカバーされていますが、この特定の質問には、UIオートメーション/ウィンドウルックアップテクニック(私は特に好きではありません)の使用に関するいくつかの回答があり、独自の作成に関する一般的な提案がありますコードを提供せずにダイアログを作成し、独自のソリューションを投稿することにしました。インスタンス化可能なMessageBox
のようなクラスを次のように作成できます:
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);
}
}
}
次に、ダイアログの参照をクラススコープに保持するか、ダイアログを表示して結果を取得するか、アプリケーションの終了イベントハンドラーでダイアログを閉じるだけです。
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();
}
最も簡単な解決策は、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);
}
メインフォームで使用する方法
private void showMessage(string msg, int interval = 0)
{
msgBox mb = new msgBox(msg, interval);
mb.ShowDialog(this);
}
あれを呼べ
showMessage("File saved");
このための独自のコントロールを作成し、必要な動作を実装します。オプションとして、このメッセージボックスを閉じるためのタイマーがある場合があります。