Закройте окно сообщений через несколько секунд



У меня есть приложение Windows Forms VS2010 C#, где я показываю MessageBox для показа сообщения.



У меня есть кнопка "ОК", но если они уйдут, я хочу тайм-аут и закрыть окно сообщения, скажем, через 5 секунд, автоматически закройте окно сообщения.



есть пользовательский MessageBox (который унаследован от формы) или другие формы репортера, но было бы интересно не нужна форма.



любые предложения или образцы о это?



обновление:



для WPF
автоматически закрыть messagebox в C#



пользовательский MessageBox (с помощью формы 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/



прокручивать Сообщение
прокручиваемый MessageBox в C#



исключение Reporter
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/206730
https://stackoverflow.com/a/14522952/206730

1038   9  

9 ответов:

попробуйте следующий подход:

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 сообщение не работает вообще.
Я предоставлю исправить в контексте отдельной AutoclosingMessageBox библиотека. Эта библиотека содержит переработанный подход и, я считаю, может быть полезна кому-то.
Он также доступен через пакета NuGet:

Install-Package AutoClosingMessageBox

примечания к выпуску (v1.0.0.2):
- Новое шоу(IWin32Owner) API для поддержки наиболее популярных сценариев (в контексте #1 );
- Новый завод () API для обеспечения полного контроля над показом MessageBox;

решение, которое работает в 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() гарантирует это, предполагая, что приведенный выше пример кода выполняется в потоке пользовательского интерфейса или созданном пользователем потоке. Пример не будет работать правильно, если код выполняется на потоке из пул потоков (например, обратный вызов таймера) или пул задач (например, для задачи, созданной с помощью TaskFactory.StartNew или Task.Run с параметрами по умолчанию).

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

теперь иди помой руки!

вы можете попробовать это:

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

Система.Окна.Почтовый ящик.Метод Show() имеет перегрузку, которая принимает окно владелец в качестве первого параметра. Если мы создадим невидимое окно владельца, которое мы затем закроем после указанного времени, это дочернее окно сообщения также закроется.

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

пока все хорошо. Но как закрыть окно, если поток пользовательского интерфейса заблокирован окном сообщения и элементы управления пользовательского интерфейса не могут быть доступны из рабочего потока? Ответ - отправив сообщение 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);
}

RogerB в CodeProject есть одно из самых гладких решений для этого ответа, и он сделал это еще в '04, и он все еще стучит

в принципе, вы зайдите сюда к своему проекту и скачайте CS файл. В случае, если эта ссылка когда-нибудь умрет, у меня есть резервная копия суть здесь. Добавьте CS-файл в свой проект или скопируйте/вставьте код где-нибудь, если вы предпочитаете это делать.

потом, все, что вам нужно сделать, это переключатель

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

до

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

и вы хорошо идти.

есть доступный проект codeproject здесь что обеспечивает эту functuanility.

после многих потоков здесь на SO и других платах это не может быть сделано с обычным MessageBox.

Edit:

У меня есть идея, которая немного эхммм да..

используйте таймер и начните, когда появится окно сообщений. Если ваш MessageBox слушает только кнопку OK (только 1 возможность) , то используйте OnTick-Event для эмуляции ESC-Press с помощью SendKeys.Send("{ESC}"); а затем остановить таймер.

код Дмитрига " получить возвращаемое значение базового 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);
}

в user32 есть недокументированный API.dll с именем MessageBoxTimeout () но для этого требуется Windows XP или более поздняя версия.

Comments

    Ничего не найдено.