数秒後にMessageBoxを閉じます


82

メッセージを表示するためのMessageBoxを表示するWindowsフォームアプリケーションVS2010C#があります。

大丈夫なボタンがありますが、ボタンが消えた場合は、タイムアウトして、たとえば5秒後にメッセージボックスを閉じ、メッセージボックスを自動的に閉じます。

カスタムMessageBox(Formから継承)または別のレポーターFormsがありますが、Formである必要はありません。

それについての提案やサンプルはありますか?

更新しました:

WPFの場合
C#でメッセージボックスを自動的に閉じる

カスタムメッセージボックス(フォーム継承を使用)
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/

スクロール可能なメッセージボックス
C#のスクロール可能なメッセージボックス

例外レポーター
/programming/49224/good-crash-reporting-library-in-c-sharp

http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework

解決:

たぶん、フォームを使わずに、次の答えが良い解決策だと思います。

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


1
これを見てください(Windowsの携帯電話が、同じでなければなりません):stackoverflow.com/questions/9674122/...
JAC

6
@istepaniuk彼が知らなければ彼は試すことができません。だからその種の質問をやめ
なさい

1
タイマーを作成し、設定した時間が経過すると閉じるように設定できるはずです
Steven Ackley 2013年

2
フォームは次のように作成できますMessageBox
spajce 2013年

2
@MustafaEkici、私は彼が何を試したかを示すためにOPを招待していました。私は彼が実際にSOに尋ねる前に試みて失敗したに違いないと思います。だからラムハウンドと私はその質問に反対票を投じた。あなたはmeta.stackexchange.com/questions/122986/
istepaniuk

回答:


123

次のアプローチを試してください。

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

AutoClosingMessageBoxクラスが次のように実装された場合:

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

更新: ユーザーがタイムアウト前に何かを選択したときに基になるMessageBoxの戻り値を取得する場合は、このコードの次のバージョンを使用できます。

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

さらに別のアップデート

@JackのケースをYesNoボタンで確認したところ、WM_CLOSEメッセージを送信するアプローチがまったく機能しないことがわかりました。別のAutocloseingMessageBoxライブラリのコンテキストで修正
を提供します。このライブラリには再設計されたアプローチが含まれており、誰かに役立つと思います。NuGetパッケージ からも入手できます

Install-Package AutoClosingMessageBox

リリースノート(v1.0.0.2):
-最も一般的なシナリオをサポートする新しいShow(IWin32Owner)API(#1のコンテキストで);
-MessageBoxの表示を完全に制御する新しいFactory()API。


System.Threading.TimerまたはSystem.Timers.Timer(@Jensの回答など)をより適切に使用しますか?SendMessageとPostMessage?
Kiquenet 2013年

@Kiquenetこの特定の状況に大きな違いはないと思います。
DmitryG 2013年

1
@GeorgeBirbilisありがとう、それは理にかなっています...この場合#32770、クラス名として値を使用できます
DmitryG 2015

2
ボタンが次の場合は機能しませんSystem.Windows.Forms.MessageBoxButtons.YesNo
Jack

1
@Jack返信が遅れて申し訳ありませんが、YesNoボタンでケースを確認しました-あなたは絶対に正しいです-それは機能しません。別のAutocloseingMessageBoxライブラリのコンテキストで修正を提供します。には再設計されたアプローチが含まれており、役立つと思います。ありがとう!
DmitryG 2017

32

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

メッセージボックスを所有するフォームを閉じると、ボックスも閉じられるという効果に基づいています。

Windowsフォームコントロールには、それらを作成したのと同じスレッドでアクセスする必要があるという要件があります。TaskScheduler.FromCurrentSynchronizationContext()上記のサンプルコードがUIスレッド、またはユーザー作成スレッドで実行されると仮定すると、を使用すると確実になります。コードは、スレッドプール(例えばAタイマーコールバック)、または(例えばで作成したタスクのタスクプールからのスレッドで実行された場合の例では、正常に動作しませんTaskFactory.StartNewか、Task.Runデフォルトのパラメータで)。


.NETのバージョンは何ですか?TaskSchedulerとは何ですか?
kiquenet 2014年

1
@Kiquenet .NET4.0以降。TaskおよびTaskSchedulerSystem.Threading.Tasksmscorlib.dllの名前空間からのものであるため、追加のアセンブリ参照は必要ありません。
BSharp 2014年

素晴らしい解決策!1つの追加...これは、新しいフォームを作成した直後に、新しいフォームにBringToFrontを追加した後、Winformsアプリでうまく機能しました。それ以外の場合、ダイアログボックスは現在アクティブなフォームの背後に表示されることがありました。つまり、ユーザーには表示されませんでした。var w = new Form(){サイズ=新しいサイズ(0、0)}; w.BringToFront();
Developer63

@ Developer63あなたの経験を再現できませんでした。w.SentToBack()直前に呼び出した場合でもMessageBox.Show()、メインフォームの上にダイアログボックスが表示されていました。.NET4.5および4.7.1でテスト済み。
BSharp 2018

このソリューションは、いいかもしれないが、それは4.0(理由Task.Delayの)を参照してください.NET 4.5でしか入手できないとまで、ありません:stackoverflow.com/questions/17717047/...
KwentRell

16

AppActivate!

参照を少し濁らせてもかまわない場合はMicrosoft.Visualbasic,、この非常に短い方法を含めて使用できます。

メッセージボックスを表示する

    (new System.Threading.Thread(CloseIt)).Start();
    MessageBox.Show("HI");

CloseIt関数:

public void CloseIt()
{
    System.Threading.Thread.Sleep(2000);
    Microsoft.VisualBasic.Interaction.AppActivate( 
         System.Diagnostics.Process.GetCurrentProcess().Id);
    System.Windows.Forms.SendKeys.SendWait(" ");
}

さあ、手を洗いに行きましょう!


11

System.Windows.MessageBox.Show()メソッドには、所有者ウィンドウを最初のパラメーターとして受け取るオーバーロードがあります。非表示の所有者ウィンドウを作成し、それを指定した時間後に閉じると、その子メッセージボックスも閉じます。

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

ここまでは順調ですね。しかし、UIスレッドがメッセージボックスによってブロックされていて、ワーカースレッドからUIコントロールにアクセスできない場合、どうすればウィンドウを閉じることができますか?答えは-WM_CLOSEウィンドウメッセージを所有者ウィンドウハンドルに送信することによって:

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

そして、これがSendMessage WindowsAPIメソッドのインポートです。

static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}

ウィンドウタイプはWindowsフォーム用ですか?
Kiquenet 2013年

非表示の親ウィンドウを閉じるためにメッセージを送信する必要があるのはなぜですか?その上で「Close」メソッドを呼び出すか、別の方法で破棄することはできませんか?
ジョージビルビリス2015

私自身の質問に答えるために、そのWPFウィンドウのOwnedWindowsプロパティは0ウィンドウを表示しているようで、Closeはメッセージボックスの子を閉じません
George Birbilis 2015

2
ブリリアントソリューション。いくつかの名前の重複がSystem.Windowsあり、System.Windows.Formsそれを理解するのに少し時間がかかりました。あなたは、次のものが必要です。SystemSystem.Runtime.InteropServicesSystem.Threading.TasksSystem.WindowsSystem.Windows.InteropSystem.Windows.Media
m3tikn0b

10

あなたはこれを試すことができます:

[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
System.Threading.TimerまたはSystem.Timers.Timer(@DmitryG回答など)をより適切に使用しますか?SendMessageとPostMessage?
キケネット2013


7

CodeProjectのRogerBは、この答えに対する最も巧妙な解決策の1つを持っています。彼は、2004年にそれを実行しましたが、それでもまだ問題はありません。

基本的に、ここで彼のプロジェクトに移動し、CSファイルをダウンロードします。そのリンクが死んだ場合に備えて、ここにバックアップの要点があります。CSファイルをプロジェクトに追加するか、必要に応じてコードをコピーしてどこかに貼り付けます。

次に、あなたがしなければならないのは切り替えるだけです

DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)

DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)

そして、あなたは行ってもいいです。


2
迅速な解決策が必要でしたが、これはうまくいきました!共有していただきありがとうございます。
マーククラム

1
例で.Show拡張子を見逃しました...次のように読みます:DialogResult result = MessageBoxEx.Show( "Text"、 "Title"、MessageBoxButtons.CHOICE、timer_ms)
Edd

2

CodeProjectのプロジェクトavaliableありHEREこのfunctuanilityを提供します。

SOや他のボードで多くのスレッドをたどると、これは通常のMessageBoxでは実行できません。

編集:

私は少しええとそうだと思います。

タイマーを使用して、MessageBoxが表示されたら開始します。メッセージボックスが[OK]ボタンのみをリッスンする場合(1つの可能性のみ)、OnTick-Eventを使用してESCをエミュレートします-を押してSendKeys.Send("{ESC}");からタイマーを停止します。


1
タイマーの概念は簡単な方法です...ただし、フォーカスがないか失われた場合は、送信されたキーがアプリにヒットすることを確認する必要があります。それにはSetForegroundWindowが必要であり、答えにはさらに多くのコードが含まれ始めますが、以下の「AppActivate」を参照してください。
fastAl 2013年

2

DMitryGのコード「基礎となる戻り値を取得するMessageBox」にはバグがあるため、timerResultが実際に正しく返されることはありません(MessageBox.Show呼び出しがOnTimerElapsed完了した後に戻ります)。私の修正は以下の通りです:

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

1

Vb.netライブラリには、このためのインタラクションクラスを使用した簡単なソリューションがあります。

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


0

EndDialog送信する代わりに使用するWM_CLOSE

[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);

0

私はこのようにしました

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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.