Предотвратить управление WebBrowser от кражи фокуса?

Есть ли способ остановить элемент управления WebBrowser от того, чтобы его родительская форма выходила на передний план?

Если вы используете метод InvokeScript для вызова функции JavaScript, которая вызывает focus() в iframe в основном родительском документе, это приведет к тому, что окно будет отображаться непосредственно на передний план (или, по крайней мере, приведет к тому, что значок панели задач начнет мигать). Есть ли способ предотвратить это?

Update:

Я нашел временный ответ на мою проблему.

Когда запускается родительское событие формы DeBefer WebBrowser, я удаляю WebBrowser из его контейнера и повторно добавляю его, когда его прежняя родительская форма активируется снова.

Это хакеры, но это работает. Однако я открыт для любых лучших предложений.

Ответ 1

РЕДАКТИРОВАТЬ: полный вопрос переписан, я неправильно понял оригинальный вопрос

Позвольте обобщить проблему: элемент управления или компонент, на который у вас нет контроля, может вызвать FlashWindow (функцию API Win32), чтобы привлечь внимание пользователя. Вы этого не хотите.

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

Ответ 2

Реализация IProtectFocus:: AllowFocusChange в объекте

Внедрите IServiceProvider на том же объекте, который реализует IOleClientSite, затем ответьте на IServiceProvider:: QueryService для SID_SProtectFocus с объектом, который предоставляет IProtectFocus

Это новый интерфейс в IE7, поэтому старым версиям не повезло.