Предотвратить контроль WebBrowser от кражи фокуса? - PullRequest
12 голосов
/ 14 октября 2009

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

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

Обновление:

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

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

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

Ответы [ 2 ]

8 голосов
/ 14 октября 2009

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

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

5 голосов
/ 23 октября 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...