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