2014-11-24 1 views
0

Приложение MDI обрабатывает обычные нажатия клавиш (A-Z, 0-9 и т. Д.) В обработчике главного окна с KeyPreview = true. Он фильтрует некоторые последовательности входящих символов, скрывая их от последующих обработчиков KeyPress. Это отлично работает в MDI-окнах, но главное окно KeyPress не вызывается, если модальное окно активно (поскольку основная форма больше не сфокусирована). Я открываю модальные окна через простые ShowDialog().Можно ли вводить любые нажатия клавиш в приложении независимо от активного окна?

Есть ли какой-нибудь способ, чтобы повсеместно поймать и фильтр KeyPress событие независимо от того, какого окна приложения имеет фокус (включая модальные окна?)

Я ищу обработку нажатий на уровне приложений, а не глобальный уровень , => Я не хочу получать уведомления о KeyPresses, если приложение не имеет фокуса.

ответ

1

Есть ли какой-нибудь способ, чтобы повсеместно поймать и фильтр KeyPress событие независимо от окна приложения имеет фокус (включая модальное окна?) Я ищу для обработки нажатий клавиш на приложениях уровня ...

Это может быть выполнено с помощью IMessageFilter().

Вот краткий пример:

public partial class MainMdiParent : Form 
{ 

    private MyFilter MF = new MyFilter(); 

    public MainMdiParent() 
    { 
     InitializeComponent(); 
    } 

    private void MainMdiParent_Load(object sender, EventArgs e) 
    { 
     MF.EscapeKey += MF_EscapeKey; 
     Application.AddMessageFilter(MF); 

     childA a = new childA(); 
     a.MdiParent = this; 
     a.Show(); 

     a = new childA(); 
     a.MdiParent = this; 
     a.Show(); 
    } 

    private void MF_EscapeKey() 
    { 
     Console.WriteLine("Escape Key Trapped in Main Form"); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     Form dlg = new Form(); 
     dlg.ShowDialog(); 
    } 

} 

public class MyFilter : IMessageFilter 
{ 

    public delegate void EscapeKeyDelegate(); 
    public event EscapeKeyDelegate EscapeKey; 

    private const int WM_KEYDOWN = 0x100; 

    public bool PreFilterMessage(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_KEYDOWN: 
       switch (m.WParam.ToInt32()) 
       { 
        case (int)Keys.Escape: 
         if (EscapeKey != null) 
         { 
          EscapeKey(); 
         } 
         return true; // suppress it? 
         break; 
       } 
       break; 

     } 

     return false; // returning false allows messages to be processed normally 
    } 

} 
+0

Мне нравится этот ответ, я даже не думал о предварительной обработке wndproc. Я думаю, у op есть два хороших варианта: – caesay

+0

@caesay - да, у меня есть :) Я уже видел эту концепцию, но я думал, что ее область действия - это форма, а не приложение. Он также позволяет обрабатывать все другие типы сообщений, что может быть полезно. – miroxlav

+0

@miroxlav: дайте нам знать, с каким способом вы пошли в конце. – caesay

1

согласно msdn система отправляет сообщения клавиатуры только на передний план, запускают ли модальные окна в другой поток? , если да, вы можете просто сообщить основную часть из потока модального окна с помощью некоторых простых потоков сообщений с использованием тех же дескрипторов, которые вы использовали в форме главного окна, теперь, если у вас нет представления о том, какой поток ... вы всегда можно узнать

здесь некоторые интересные статьи, которые могут помочь вам about keyboards

about threads and msgs

дополнительного примечание: вы всегда можете использовать sendkeys функции для отправки ключей на другую цель, если это необходимо

Редактировать: Showdialog - это блокирующий асинхронный вызов, он «замораживает» поток и другие окна и возвращается только тогда, когда оконное окно было закрыто, поэтому ручки не реагируют, когда активировано модальное окно.

+0

Я использую классический 'ShowDialog()', чтобы открыть модальные окна. На данный момент я не уверен, откроет ли оно модальное окно в отдельном потоке. – miroxlav

+0

ha это он человек, здесь открывается showDialog, при запуске он отключает все остальные окна, поэтому главное окно ничего не может сделать, вы должны найти другой способ запуска модального окна. –

+0

Когда экземпляр класса Window создается, он по умолчанию не отображается. ShowDialog показывает окно, отключает все остальные окна приложения и возвращается только при закрытии окна. Этот тип окна известен как модальное окно, непосредственно из [link] (http://msdn.microsoft.com/en-us/library/system.windows.window.showdialog (v = vs.110) .aspx) –

2

Проблема заключается в том, что при вызове ShowDialog ваш основной поток пользовательского интерфейса прекратит отправку сообщений. Посмотрите на этом упрощенном цикле сообщений:

while(true) 
{ 
    Message m; 
    GetMessage(out m); 
    // if close button pressed etc. 
    if (m.Msg == WM_QUIT) break; 
    DispatchMessage(m); 
} 

При активации вашего диалога, это выполняется в DispatchMessage и начинает новый цикл обработки сообщений, который подобен. Из-за этого нового цикла цикл из вашего главного окна блокируется при вызове DispatchMessage и не будет обрабатывать любые сообщения клавиатуры. Это сложная работа с несколькими контурами пользовательского интерфейса, и по этой причине я бы рекомендовал использовать регулярное сообщение Show() (которое не будет блокировать ваш основной цикл приложения) и найти другой способ настроить ориентацию пользователей на это окно.

Существует вызов ap/invoke, который я использовал в прошлом, который устанавливает родительское окно как отключенное и направляет фокус пользователя на дочернее окно, он почти идентичен поведению вызова ShowDialog, но без блокировки потока , То, что вы сделали бы, это установить владельца вашей дочерней формы, вызвать обычный метод Show() и, наконец, установить включенное состояние родительского окна в значение false. Вы можете сделать позже с этим кодом:

const int GWL_STYLE = -16; 
const int WS_DISABLED = 0x08000000; 

[DllImport("user32.dll")] 
static extern int GetWindowLong(IntPtr hWnd, int nIndex); 

[DllImport("user32.dll")] 
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

void SetWindowEnabled(Form form, bool enabled) 
{ 
    SetWindowLong(form.Handle, GWL_STYLE, GetWindowLong(form.Handle, GWL_STYLE) & 
        ~WS_DISABLED | (enabled ? 0 : WS_DISABLED)); 
} 

Касание на ваш комментарий о сложенных модели окон, я думаю, что это возможно, но это очень важно, чтобы убедиться, что нативный метод вызывается повторно включить родительское окно когда диалог закрыт.Я хотел бы реализовать метод расширения, который выглядит примерно так:

public static class FormsExtensions 
{ 
    public static Task<bool> ShowNativeDialog(this Form child, Form owner) 
    { 
     var tcs = new TaskCompletionSource<bool>(); 
     child.Show(); 
     SetWindowEnabled(owner, false); 
     child.Closed += (sender, args) => { 
      SetWindowEnabled(owner, true); 
      tcs.SetResult(true); 
     } 
     return tcs.Task; 
    } 
} 

А использование будет выглядеть примерно так:

DialogForm dialog = new DialogForm(); 
await dialog.ShowNativeDialog(this); 

Используя Await, вы можете остановить поток выполнения без блокировки цикла UI сообщений ,

+0

Интересующийся ответ. Позвольте мне попытаться реализовать этот подход ... Мне любопытно узнать, хорошо ли это будет работать даже со сложными модальными окнами (модальный диалог открыт из модального диалога). – miroxlav