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

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

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

обновление:

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

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

Это своего рода hacky, но это работает. Но я готов выслушать любые предложения получше.

2 ответов


EDIT: полный вопрос переписан, я неправильно понял оригинальный вопрос

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

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

FlashWindow

Microsoft не объясняет так много слов, что FlashWindow делает. К сожалению, он не отправляет конкретное сообщение (скажем WM_FLASH или аналогичный), что облегчило бы захват и аннулирование этого поведения. Вместо FlashWindow делает три вещи:

  1. он устанавливает системный таймер для мигания интервалы
  2. передает WM_NCACTIVATE сообщение для первой вспышки
  3. передает WM_NCACTIVATE сообщение, когда таймер истекает (при получении WM_SYSTIMER)

в зависимости от того, как компонент вызывает FlashWindow, это может быть неопределенным, пока не наступит другой тайм-аут, пока он не сфокусируется или только один раз. Каждое сообщение WM_NCACTIVATE активирует или деактивирует NC-зону (строка заголовка, кнопка на панели задач). Это не меняет ввод фокуса.

вызов

любое решение для предотвращения мигания немного участвует. Основная проблема являются:

  1. the WM_SYSTIMER событие отправляется асинхронно с PostMessage и не принимается WndProc метод формы (он обрабатывает только синхронные сообщения)
  2. the WM_NCACTIVATE сообщения также используются, когда пользователь нажимает на строку заголовка или кнопку панели задач, чтобы установить фокус ввода, просто отмена этих сообщений будет иметь нежелательные побочные эффекты
  3. 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, the 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
    }
}

чтобы активировать этот messagefilter, просто добавьте следующую строку где-нибудь в событие загрузки формы:

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, поэтому старые версии не повезло.