Предотвратить контроль WebBrowser от кражи фокуса?
Есть ли способ не дать элементу управления WebBrowser заставить его родительскую форму вывести себя на передний план?
Если вы используете метод InvokeScript для вызова функции JavaScript, которая вызывает focus() для iframe в главном родительском документе, это приведет к тому, что окно переместится прямо вперед (или, по крайней мере, заставит значок панели задач мигать). Есть ли способ предотвратить это?
Обновить:
Я нашел временный ответ на мою проблему.
Когда происходит событие Deactive родительской формы WebBrowser, я удаляю WebBrowser из его контейнера и повторно добавляю его, когда его старая родительская форма снова активируется.
Это отчасти хакерски, но это работает. Я открыт для любых лучших предложений, хотя.
2 ответа
РЕДАКТИРОВАТЬ: полный вопрос переписан, я неправильно понял оригинальный вопрос
Давайте обобщим проблему: элемент управления или компонент, над которым у вас нет контроля, может вызвать FlashWindow
(функция Win32 API), чтобы привлечь внимание пользователя. Вы этого не хотите.
Для этого обычно есть два решения: использовать перехват API или перехват сообщений. Поскольку перехват API сложен и сложен, я представлю решение для перехвата сообщений.
FlashWindow
Microsoft не объясняет так много слов, что FlashWindow
делает. К сожалению, он не отправляет конкретное сообщение (скажем, WM_FLASH
или аналогичный), который бы облегчил захват и аннулирование этого поведения. Вместо, FlashWindow
делает три вещи:
- Устанавливает системный таймер для интервалов мигания
- Отправляет
WM_NCACTIVATE
сообщение для первой вспышки - Отправляет
WM_NCACTIVATE
сообщение об истечении времени таймера (при полученииWM_SYSTIMER
)
В зависимости от того, как компонент вызывает FlashWindow, это может быть неопределенным, пока не произойдет другой тайм-аут, пока у него не будет фокуса или только один раз. Каждое сообщение WM_NCACTIVATE активирует или деактивирует NC-зону (строка заголовка, кнопка на панели задач). Это не меняет фокус ввода.
Вызов
Любое решение для предотвращения перепрошивки немного вовлечено. Основными проблемами являются:
WM_SYSTIMER
событие отправляется асинхронно с PostMessage и не получаетWndProc
метод формы (обрабатывает только синхронные сообщения)WM_NCACTIVATE
Сообщения также используются, когда пользователь нажимает на строку заголовка или кнопку панели задач, чтобы установить фокус ввода, просто отмена этих сообщений будет иметь нежелательные побочные эффекты- FlashWindow всегда будет мигать хотя бы один раз, независимо от
WM_SYSTIMER
стрельба или нет.
WM_SYSTIMER
сообщение недокументировано Имеет значение 0x0118
и используется внутри Windows для определения времени таких вещей, как мигание каретки, задержка открытия меню и т. д. Здесь оно используется для промежутка времени между вспышками.
Решение
Решение, которое я представляю здесь, является основой для дальнейшего развития. Это не полное решение, но оно решает проблему во многих случаях. Поместите следующее в код формы:
protected override void WndProc(ref Message m)
{
bool messageHandled = false;
if (m.Msg == WM_NCACTIVATE)
{
// add logic here to determine user action, losing focus etc and set
// messageHandled and m.Result only when user action is not the cause
// of triggering WM_NCACTIVATE
m.Result = IntPtr.Zero;
messageHandled = true;
}
if(!messageHandled)
base.WndProc(ref m);
}
Приведенный выше код уже полностью предотвращает перепрошивку. Вам придется добавить немного логики, чтобы изменить строку заголовка, потому что полностью игнорируя WM_NCACTIVATE
означает, что строка заголовка будет выглядеть активной все время, даже если это не так.
Следующий код дает вам больше контроля. Вы можете использовать его, чтобы реагировать на саму вспышку. Обычно главное окно не получает WM_SYSTIMER
события так часто, но вам придется экспериментировать, должны ли вы делать исключения. Кажется, что для FlashWindow
, wParam
всегда установлен на 0xFFF8
, но поэкспериментируйте с этим, так как это нигде не задокументировано.
public class MyMessageFilter : IMessageFilter
{
// an application can have many windows, only filter for one window at the time
IntPtr FilteredHwnd = IntPtr.Zero;
public MyMessageFilter(IntPtr hwnd)
{
this.FilteredHwnd = hwnd;
}
public bool PreFilterMessage(ref Message m)
{
if (this.FilteredHwnd == m.HWnd && m.Msg == WM_SYSTIMER)
return true; // stop handling the message further
else
return false; // all other msgs: handle them
}
}
Чтобы активировать этот фильтр сообщений, просто добавьте следующую строку в событие загрузки формы:
Application.AddMessageFilter(new MyMessageFilter(this.Handle));
Следующие константы должны быть размещены на уровне класса. Они используются в обоих разделах кода выше:
public const UInt32 WM_SYSTIMER = 0x0118;
public const UInt32 WM_NCACTIVATE = 0x86;
Заключение
Хотя сама проблема разрешима, это далеко не просто. С вышеупомянутыми ручками вы должны пройти довольно далеко. Используйте фильтр, чтобы предотвратить мигание, но тогда все еще происходит первая "вспышка". Использовать WinProc
переопределите, чтобы предотвратить первый, но добавьте некоторую логику, чтобы приложение не работало слишком странно (то есть: всегда неактивная строка заголовка или всегда активная). У вас уже есть код, который вы можете объединить с этим, чтобы установить некоторые логические флаги.
Реализация IProtectFocus::AllowFocusChange в объекте
Реализуйте IServiceProvider на том же объекте, который реализует IOleClientSite, затем ответьте на IServiceProvider::QueryService для SID_SProtectFocus с объектом, который предоставляет IProtectFocus
Это новый интерфейс в IE7, поэтому старым версиям не повезло.