メッセージを表示するためのMessageBoxを表示するWindows FormsアプリケーションVS2010 C#があります。
私は大丈夫なボタンを持っていますが、彼らが立ち去った場合、タイムアウトして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
スクロール可能なメッセージボックス
C#のスクロール可能なメッセージボックス
例外レポーター
https://stackoverflow.com/questions/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/2067
https://stackoverflow.com/a/14522952/2067
次のアプローチを試してください。
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);
}
Update:ユーザーがタイムアウト前に何かを選択したときに、基礎となるメッセージボックスの戻り値を取得したい場合、このコードの次のバージョンを使用できます。
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
メッセージを送信するアプローチがまったく機能しないことを発見しました。
個別の AutoclosingMessageBox ライブラリのコンテキストでfixを提供します。このライブラリには再設計されたアプローチが含まれており、誰かに役立つと思います。
- NuGetパッケージ からも利用できます。
Install-Package AutoClosingMessageBox
リリースノート(v1.0.0.2):
-最も一般的なシナリオをサポートする新しいShow(IWin32Owner)API( #1 のコンテキストで);
-MessageBoxの表示を完全に制御できる新しいFactory()API。
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スレッドまたはユーザーが作成したスレッドで実行されると仮定して、確実になります。コードがスレッドプール(タイマーコールバックなど)またはタスクプール(たとえば、TaskFactory.StartNew
またはTask.Run
でデフォルトパラメーターを使用して作成されたタスク)のスレッドで実行される場合、この例は正しく動作しません。 。
参照を少し汚すことを気にしない場合は、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(" ");
}
さあ、手を洗ってください!
System.Windows.MessageBox.Show()メソッドには、所有者Windowを最初のパラメーターとして受け取るオーバーロードがあります。指定された時間後に閉じる非表示の所有者ウィンドウを作成すると、子メッセージボックスも閉じます。
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 Windows APIメソッドのインポートを示します。
static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
これを試すことができます:
[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 CodeProjectの終わりには、この答えに対する最も洗練された解決策の1つがあります。
基本的に、あなたは 彼のプロジェクトに行き、CSファイルをダウンロードします 。リンクが切れた場合に備えて、バックアップを用意しています Gist here。 CSファイルをプロジェクトに追加するか、必要に応じてコードをどこかにコピーして貼り付けます。
その後、あなたがしなければならないのはスイッチです
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
に
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
そして、あなたは行ってもいいです。
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);
}
利用可能なcodeprojectプロジェクトがあります HERE この機能を提供します。
SOおよびその他のボード上の多くのスレッドに続いて、これは通常のMessageBoxでは行えません。
編集:
私はちょっとええと考えている...
タイマーを使用して、MessageBoxが表示されたら開始します。 MessageBoxがOKボタンのみをリッスンする場合(1つの可能性のみ)、OnTick-Eventを使用してSendKeys.Send("{ESC}");
でESC-Pressをエミュレートし、タイマーを停止します。
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);
}
WM_CLOSE
を送信する代わりにEndDialog
を使用します。
[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
私はこうしました
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);
User32.dllにはMessageBoxTimeout()という名前の文書化されていないAPIがありますが、Windows XP以降が必要です。