Прымусова закрыць MessageBox праграмна

Дазвольце мне даць вам фон.

У нас ёсць дадатак (сярэдняга памеру), які выкарыстоўвае MessageBox.Show (....) у розных месцах (у сотнях).

Гэтыя вокны паведамленняў з'яўляюцца часткай worklfow і выкарыстоўваюцца для інфармавання, папярэджання або прымаць ўваходны сігнал ад карыстальніка. Прыкладанне павінна аўтаматычна выйсці па заканчэнні пэўнага часу, калі няма ніякай актыўнасці. У нас ёсць патрабаванне, што пры ўваходзе з прыкладання, проста каб ачысціць дадзеныя сесіі, каб ачысціць погляды і схаваць сябе, так што ў наступным запуску, ён не павінен будзе выканаць працэс запуску, які з'яўляецца дарагім з пункту гледжання часу.

Усё працуе нармальна, але ў выпадку, калі ёсць акно паведамленні на экране і карыстальнік пакінуў машыну, не адказваючы на ​​акно паведамленні, а затым з-за адсутнасць актыўнасці, каб зрабіць прыкладанне для выхаду з сістэмы. Праблема акно Паведамленне не знікне.

Як я магу зачыніць адкрытую MessageBox, калі такія маюцца, у той час як хаваць дадатак?

Дзякуючы.

14
Я думаў, што MessageBox.Show (...) мадальнай, так як можа праграма паслаць ключ? Карыстаецеся Ці вы Threads/задачы?
дададзена аўтар Fischermaen, крыніца
Можа паслаць ключ ўвесці або ESC? :)
дададзена аўтар Reniuz, крыніца
Дзякуй за адказы. Проста для ўдакладнення, выкарыстоўваючы індывідуальнае Паведамі поле не варыянт, як пераробкі даволі вялікія. Адпраўка ключа ESC таксама ня правільна, таму што толькі актыўнае ўжыванне атрымае каманду. Я выкарыстоўваю FindWindow падыход, пры якім я атрымліваю Msgbox справіцца з прапусканнем ID і паведамілі акно подпісы. Пасля таго, як апрацоўшчык я заплюшчваю з дапамогай наступнай win32 API, напрыклад, SendMessage (новы HandleRef (нуль, msgbxcHandler), WM_CLOSE, IntPtr.Zero, IntPtr.Zero); SendMessage (новы HandleRef (нуль, msgbxcHandler), WM_NCDESTROY, IntPtr.Zero, IntPtr.Zero); Да гэтага часу яго працуе нармальна.
дададзена аўтар NYK, крыніца

9 адказы

Вось кавалак кода на аснове UIAutomation (прахалодны, але ўсё яшчэ не вельмі прывыклі API), што спробы закрыць усе мадальныя акна (у тым ліку адзін адкрыты з MessageBox) бягучага працэс:

    /// 
    /// Attempt to close modal windows if there are any.
    /// 
    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();
                    }
                }
            }
        }
    }

Напрыклад, калі ў вас ёсць прыкладанне WinForms, якое з'яўляецца ў MessageBox пры націску якой-button1, вы ўсё роўна будзеце ў стане закрыць прыкладанне з дапамогай меню Windows «Close Window» (пстрыкніце правай кнопкай мышы на панэлі задач):

    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 дзе-небудзь яшчэ ў вашым кодзе, вядома, гэта ўсяго толькі прыклад.

7
дададзена
Я меў на ўвазе, аднак, да .net API, які гэта пра ...
дададзена аўтар Erik Funkenbusch, крыніца
Прастору імёнаў System.Windows.Automation кажа, што гэта для WPF. msdn.microsoft.com/en-us/library /ms747327(v=vs.110).aspx "Microsoft UI Automation з'яўляецца новая структура даступнасці для Microsoft Windows, даступныя на ўсіх аперацыйных сістэмах, якія падтрымліваюць Windows Presentation Foundation (WPF)."
дададзена аўтар Erik Funkenbusch, крыніца
Ня UIAutomation для WPF? Пытанне было для WinForms
дададзена аўтар Erik Funkenbusch, крыніца
@ErikFunkenbusch - Не ведаю, што вы маеце на ўвазе «для WPF». UIAutomation падтрымлівае любыя прыкладання.
дададзена аўтар Simon Mourier, крыніца
Можа быць, але UIAutomation не абмяжоўваецца WPF, альбо ў якасці кліента або сервера. У гэтым жа артыкуле гаворыцца наступнае: «Распрацоўнікі інтэрфейсу пастаўшчыкоў аўтаматызацыі для іншых структур, чым WPF.»
дададзена аўтар Simon Mourier, крыніца
Дзякуй, мне вельмі дапамог.
дададзена аўтар digitguy, крыніца

Гэта спасылка на форумах MSDN паказвае, як зачыніць акно паведамлення, выкарыстоўваючы FindWindow і адпраўка WM_CLOSE паведамленне. Хоць пытанне было зададзена для .NET/WindowsCE, гэта можа вырашыць вашу праблему, яе варта паглядзець

6
дададзена
Я думаю, што гэта лічыцца добрай практыкай, каб дадаць па меншай меры, зусім крыху дадатковай інфармацыі аб тым, як звязаная старонка можа вырашыць праблему (замест таго, каб проста размясціць голую спасылку). Я спадзяюся, вы не пярэчыце, што я дадаў кароткае апісанне. +1 ў любым выпадку, гэта можа быць карысным рэсурсам.
дададзена аўтар MartinStettner, крыніца
Ну Марцін, вы абсалютна правы. у сувязі са складанасцю часу я не быў у стане дадаць гэтую інфармацыю. Аднак дзякуючы для рэдагавання. :)
дададзена аўтар Bravo, крыніца

Першае пытанне: Калі паведамлення скрынкі выкарыстоўваюцца як частка працоўнага працэсу, не зачыняючы акно праграмна паведамленне выклікае паток, каб змяніць/працягнуць?

Я думаю, што ў вас ёсць тры варыянты

  1. Стварыце сваю ўласную версію класа MessageBox, якая адкрывае дыялогавае акно, якое выглядае як MessageBox з дадатковай функцыянальнасцю, так што зачыняецца аўтаматычна пасля пэўнага перыяду часу.

  2. Рэалізаваць нешта падобнае ў C#, каб закрыць акно паведамленняў імі праграмным. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. Пазбаўцеся ад акна паведамленні ад interupting працоўнага працэсу. Гэта, верагодна, самае лепшае рашэнне, бо ад гуку яго закрыцця акна паведамлення праграмна выкліча працоўны працэс, каб працягнуць/змяненне, і, магчыма, нават выклікаць іншы MessageBox, каб паказаць, што не можа быць пажаданым. Але, відавочна, фіксуючы корань праблемы можа быць лепш, але гэта не заўсёды самы просты.

1 і 2 павінны былі б зрабіць з асобнага патоку, так што вам трэба будзе думаць пра наступствы, якія, як паказваючы MessageBox будзе блакаванне.

3
дададзена
+1 да кропкі 3.! Я таксама, я б не мець добрае пачуццё проста «якое пацвярджае» любое адкрытае акно паведамленні, не ведаючы нават, калі карыстальнік заўважыў ...
дададзена аўтар MartinStettner, крыніца

Heres мой прыклад з SendKeys - праверана і працуе:

дазваляе сказаць, што мы маем BackgroundWorker і кнопкі ў форме. Пасля таго, як кнопка была кнопка мышы - запусціць працоўны і паказаць акно паведамленні. У працоўным DoWork сне падзеі для 5s, а затым адправіць ўвесці ключ - messsage акно закрытая.

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
}
2
дададзена
Гэта рашэнне працуе толькі калі карыстальнік не мяняе фокус на іншае прыкладанне. Вы не можаце быць упэўнены, што прыкладанне ўсё яшчэ мае фокус. І Windows, прадухіляе дадатак з праграмным прымаць фокус іншага прыкладання.
дададзена аўтар Alex, крыніца
Я проста хачу адзначыць, што для вырашэння вельмі важна, каб праверыць, ці ёсць на самой справе акно з паведамленнем адкрытым: ключ ўваходу адпраўлены на любую іншую частку карыстацкага інтэрфейсу можа мець непажаданыя наступствы ...
дададзена аўтар MartinStettner, крыніца
Ды ваша кропка добра. Яго хуткі абыходны шлях так ўвесці ключ, відавочна, не добры ключ, лепш выкарыстоўваць ESC - у UI яна павінна прадстаўляць гашэнне.
дададзена аўтар Reniuz, крыніца

Прымаючы ў якасці здагадкі, што вы можаце рэдагаваць код, які тэлефануе ў <Код> MessageBox.Show() метад, я б рэкамендаваў не выкарыстоўваць MessageBox. Замест гэтага, проста выкарыстоўваць свае ўласныя прыстасаваныя формы, выклік ShowDialog() на ім, каб зрабіць у прынцыпе тое ж самае, што і клас MessageBox. Потым ты ёсць асобнік самой формы, і вы можаце выклікаць Close() на што напрыклад, каб закрыць яго.

Добрым прыкладам можа служыць .

1
дададзена
Нав. І мы бачым шмат разоў, як гэта жахліва, калі людзі переописать акно паведамленняў. <Код> тзд паказвае, што вы можаце мець рэальнае акно паведамленні і закрыць яго пасля затрымкі ...
дададзена аўтар Joey, крыніца
@Joey: так, вы маеце рацыю ... гэта была проста ідэя, а не лепш, напэўна. Вы кажаце, што <я> тзд паказвае, што вы можаце мець рэальнае акно паведамленні і закрыць яго пасля затрымкі ... што ці хто з'яўляецца «паведамленне»? На жаль, я не разумею, я прашу прабачэння
дададзена аўтар Marco, крыніца

Я думаю, што чысты шлях будзе рэалізаваць вам ўласную форму акна паведамлення, як

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

 //...
}

У некаторых з маіх вялікіх праектаў, я знайшоў гэты падыход карысны і для іншых мэтаў (напрыклад, аўтаматычная рэгістрацыі паведамленняў пра памылкі і г.д.)

Другая ідэя ў мяне была б выкарыстоўваць GetTopWindow() (ці, магчыма, некаторыя іншыя функцыі WIN32), каб атрымаць бягучае акно верхняга ўзроўню вашага прыкладання і адправіць WM_CLOSE паведамлення да яго.

1
дададзена
што, калі ўкладка альт карыстальнік націскае кнопку або іншае акно паведамленне, паказанае вышэй патрабаванага акна паведамлення. у гэтым выпадку я мяркую, што ён закрые непажаданую адзін.
дададзена аўтар Vivekh, крыніца

Refer to DmitryG post in "Close a MessageBox after several seconds"

Auto-Close MessageBox пасля тайм-аўту дасяжнасці

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);
1
дададзена

Я выкарыстаў .net 2 і два падыходу з тым жа трукам.

Open the MessageBox from stub-Form with MessageBox.Show(this,"message")

Калі форма не бачная ці не мае на самай справе UI.

  1. Keep the form handler and close it with:

    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    

    or

  2. holding the form as class parameter and using FormX.Close().

Паколькі форма з'яўляецца ўладальнікам MessageBox, закрыццё будзе зачыніць MessageBox.

1
дададзена

Стварыце свой уласны кантроль за гэта і рэалізаваць паводзіны, якое вы хочаце мець там. У якасці опцыі можа быць таймер, каб закрыць MessageBox.

0
дададзена