몇 초 후 MessageBox 닫기


82

메시지를 표시하기 위해 MessageBox를 표시하는 Windows Forms 응용 프로그램 VS2010 C #이 있습니다.

괜찮은 버튼이 있지만, 그들이 떠나면 시간 초과를하고 5 초 후에 메시지 상자를 닫고 메시지 상자를 자동으로 닫고 싶습니다.

사용자 지정 MessageBox (Form에서 상 속됨) 또는 다른 리포터 Forms가 있지만 Form이 필요하지 않은 것은 흥미로울 것입니다.

그것에 대한 제안이나 샘플이 있습니까?

업데이트 :

WPF의 경우
C #에서 자동으로 메시지 상자 닫기

Custom MessageBox (Form inherit 사용)
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/

Scrollable MessageBox
C #의 스크롤 가능한 MessageBox

예외 기자
/programming/49224/good-crash-reporting-library-in-c-sharp

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

해결책:

아마도 Form을 사용하지 않고 다음 답변이 좋은 해결책이라고 생각합니다.

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


1
이것 좀보세요 (Windows Phone이지만 동일해야 함) : stackoverflow.com/questions/9674122/…
jAC

6
@istepaniuk 그가 모르는 경우 시도 할 수 없습니다. 그래서 질문의 종류 중지
무스타파 에키

1
당신은 타이머를 생성 할 수 있어야하고, 일정 시간 후에 종료를 설정해야합니다
스티븐 애 클리

2
양식을 다음과 같이 만들 수 있습니다.MessageBox
spajce

2
@MustafaEkici, 나는 그가 시도한 것을 보여주기 위해 OP를 초대했습니다. 나는 그가 실제로 그렇게 요청하기 전에 시도하고 실패했을 것이라고 가정합니다. 그것이 Ramhound와 내가 질문에 반대 한 이유입니다. 당신은 읽을 수 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);
}

또 다른 업데이트

YesNo버튼으로 @Jack의 경우를 확인한 결과 WM_CLOSE메시지 를 보내는 방법 이 전혀 작동하지 않는다는 것을 발견했습니다 . 별도의 AutoclosingMessageBox 라이브러리 컨텍스트에서 수정 사항
제공합니다 . 이 라이브러리에는 재 설계된 접근 방식이 포함되어 있으며 누군가에게 유용 할 수 있다고 생각합니다. NuGet 패키지 를 통해서도 사용할 수 있습니다 .

Install-Package AutoClosingMessageBox

릴리스 정보 (v1.0.0.2) :
-가장 인기있는 시나리오를 지원하는 새로운 Show (IWin32Owner) API ( # 1 컨텍스트에서 );
-MessageBox 표시에 대한 모든 권한을 제공하는 New Factory () API;


System.Threading.Timer 또는 System.Timers.Timer (@Jens 대답과 같은)를 더 잘 사용합니까? SendMessage 대 PostMessage?
Kiquenet

@Kiquenet 나는이 특정 상황에서 큰 차이가 없다고 생각합니다.
DmitryG 2013 년

1
@GeorgeBirbilis 감사합니다, 그것은 ... 감각을 만들 수 있습니다이 경우 사용할 수있는 #32770클래스 이름으로 값을
DmitryG

2
버튼이 있으면 작동하지 않습니다System.Windows.Forms.MessageBoxButtons.YesNo
Jack

1
@Jack 답장이 늦어 죄송합니다. YesNo버튼으로 케이스를 확인했습니다 -당신이 절대적으로 맞습니다-작동하지 않습니다. 별도의 AutoclosingMessageBox 라이브러리 컨텍스트에서 수정 사항을 제공 할 것 입니다. 에는 재 설계된 접근 방식이 포함되어 있으며 유용 할 수 있다고 생각합니다. 감사!
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 Forms 컨트롤에는 해당 컨트롤을 만든 동일한 스레드에서 액세스해야한다는 요구 사항이 있습니다. 을 사용 TaskScheduler.FromCurrentSynchronizationContext()하면 위의 예제 코드가 UI 스레드 또는 사용자가 만든 스레드에서 실행된다고 가정합니다. 코드가 스레드 풀 (예 : 타이머 콜백) 또는 작업 풀 (예 : 기본 매개 변수로 생성 TaskFactory.StartNew되거나 Task.Run기본 매개 변수로 생성 된 작업)에서 스레드에서 실행되는 경우 예제가 올바르게 작동하지 않습니다 .


.NET 버전은 무엇입니까? TaskScheduler 란 무엇입니까?
Kiquenet 2014 년

1
@Kiquenet .NET 4.0 이상. TaskTaskScheduler네임 스페이스입니다 System.Threading.Tasks추가 어셈블리 참조가 필요하지 않도록가 mscorlib.dll에서.
BSharp

훌륭한 솔루션! 한 가지 추가 사항은 새 양식을 만든 직후에 BringToFront를 추가 한 후 Winforms 앱에서 잘 작동했습니다. 그렇지 않으면 대화 상자 가 현재 활성 양식 뒤에 표시되는 경우가 있습니다. 즉, 사용자에게 표시되지 않았습니다. var w = new Form () {Size = new Size (0, 0)}; w.BringToFront ();
Developer63

@ Developer63 귀하의 경험을 재현 할 수 없습니다. w.SentToBack()직전에 호출하더라도 MessageBox.Show()대화 상자는 여전히 기본 양식 위에 표시됩니다. .NET 4.5 및 4.7.1에서 테스트되었습니다.
BSharp

이 솔루션은 훌륭 할 수 있지만 4.0이 아닌 .NET 4.5 이상에서만 사용할 수 있습니다 (Task.Delay로 인해) 참조 : stackoverflow.com/questions/17717047/…
KwentRell

16

AppActivate!

참고 문헌을 조금 헷갈 리게해도 괜찮다면 Microsoft.Visualbasic,이 방법을 아주 짧은 방법으로 포함 하고 사용할 수 있습니다 .

MessageBox 표시

    (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를 첫 번째 매개 변수로 사용하는 오버로드가 있습니다. 보이지 않는 소유자 창을 만들고 지정된 시간 후에 닫으면 자식 메시지 상자도 닫힙니다.

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

여태까지는 그런대로 잘됐다. 하지만 UI 스레드가 메시지 상자에 의해 차단되고 작업자 스레드에서 UI 컨트롤에 액세스 할 수없는 경우 어떻게 창을 닫을까요? 대답은-소유자 창 핸들에 WM_CLOSE windows 메시지를 보내는 것입니다.

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

창 유형은 Windows Forms입니까?
Kiquenet 2013

닫으려면 숨겨진 상위 창에 메시지를 보내야하는 이유는 무엇입니까? "Close"메서드를 호출하거나 다른 방법으로 처리 할 수 ​​없습니까?
George Birbilis

내 자신의 질문에 대답하기 위해, 그 WPF 윈도우의 OwnedWindows 속성은 0 창을 표시 할 수 있도록 보인다 닫기 닫지 메시지 박스 아이를 않습니다
조지 Birbilis

2
화려한 솔루션. 거기에 일부 명명 중복입니다 System.WindowsSystem.Windows.Forms그 알아 내기 위해 좀 시간이 걸렸습니다. 다음과 같은 필요합니다 System, System.Runtime.InteropServices, System.Threading.Tasks, System.Windows, System.Windows.Interop,System.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?
Kiquenet 2013

1
참조 stackoverflow.com/questions/3376619/...을 어쩌면 또한 stackoverflow.com/questions/2411116/... sendMessage 첨부와 PostMessage를 사이의 차이에
조지 Birbilis

7

CodeProject의 RogerB 는이 답변에 대한 가장 매끄러운 솔루션 중 하나를 가지고 있으며, 그는 '04 년에 그렇게했습니다.

기본적으로 여기에서 그의 프로젝트로 이동하여 CS 파일을 다운로드합니다 . 그 링크가 죽는 경우를 대비하여 여기에 백업 요점이 있습니다. 프로젝트에 CS 파일을 추가하거나 원하는 경우 코드를 복사 / 붙여 넣기하십시오.

그런 다음 전환하기 만하면됩니다.

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

...에

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

그리고 당신은 갈 수 있습니다.


2
나는 빠른 해결책이 필요했고 이것은 훌륭하게 작동했습니다! 공유해 주셔서 감사합니다.
Mark Kram

1
예제에서 .Show 확장자를 놓쳤습니다 ... 읽어야합니다. DialogResult result = MessageBoxEx.Show ( "Text", "Title", MessageBoxButtons.CHOICE, timer_ms)
Edd

2

여기에 사용 가능한 코드 프로젝트 프로젝트가 있습니다. 이 functuanility을 제공합니다.

SO 및 기타 보드에서 많은 스레드를 따라 가면 일반 MessageBox로는 수행 할 수 없습니다.

편집하다:

약간 음 .. 네 ..

타이머를 사용하고 MessageBox가 나타나면 시작하십시오. MessageBox가 OK 버튼 만 듣는 경우 (하나만 가능) OnTick-Event를 사용하여 ESC-Press를 에뮬레이트 한 SendKeys.Send("{ESC}");다음 타이머를 중지합니다.


1
타이머 개념은 간단한 방법입니다 ...하지만 포커스가 없거나 포커스를 잃은 경우 전송 된 키가 앱에 도달하도록해야합니다. 이를 위해서는 SetForegroundWindow가 필요하고 답변에 더 많은 코드가 포함되기 시작하지만 아래의 'AppActivate'를 참조하십시오.
FastAl 2013 년

2

DMitryG의 코드 "기본의 반환 값 가져 오기 MessageBox"에는 버그가 있으므로 timerResult는 실제로 제대로 반환되지 않습니다 ( MessageBox.Show호출은 AFTER 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

user32.dll에 MessageBoxTimeout ()이라는 문서화되지 않은 API가 있지만 Windows XP 이상이 필요합니다.


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.