Программируем панель задач Windows 7: панель миниатюр
Пришло время вернуться к привычному описанию Windows 7 API. Мы уже рассмотрели основы программирования панели задач Windows 7 в статье Разработка для панели задач Windows 7: AppID, а также способы создания списка переходов для своего приложения в статье Программируем панель задач Windows 7: списки переходов (ч.1), (ч.2) и (ч.3). В сегодняшней статье мы сосредоточимся на использовании панели миниатюр, которые позволят повысить эффективность работы пользователя.
Чтобы убедиться, что все читатели понимают концепцию, давайте вкратце расскажем о них. Если вы уже знакомы с панелью миниатюр, тогда можете свободно переходить к середине статье, где приведены фрагменты реального кода.
Как сказано выше, панели миниатюр – отличный способ увеличения производительности труда пользователя, позволяющий использовать функции приложения, даже в тех случаях, когда приложение находится в фоне. В частности, с его помощью можно получить доступ к приложению без переключения на него (не делая его активным). Панели миниатюр представляют собой набор, состоящих из семи или менее кнопок, размещенных внизу интерактивной миниатюры на панели задач.
Отличным примером является панель миниатюр Windows Media Player, показанная ниже. Если подвести указатель мыши к иконке Media Player, когда приложение запущено, вы увидите три кнопки на дисплее: Play/Pause, Next и Previous. Нажатие на каждую кнопку выполнит соответствующую команду.
Еще одним хорошим примером, о котором мы упоминали несколько недель назад, является приложение Fishbowl для Facebook. Это приложение использует панель миниатюр для быстрого доступа к основным функциям, которые выполняет каждый пользователь Facebook. Как видите на следующем снимке экрана, на панели миниатюр Fishbowl пять кнопок (фактически их шесть, но об этом чуть позже).
Теперь, когда мы увидели преимущества новой функции, давайте посмотрим, как можно ее реализовать. Во-первых, несколько основных правил:
- Можно отобразить до семи кнопок с фиксированным размером в 16x16 пикселей (или в 24x14 пикселей для высокого DPI);
- Можно назначить кнопку панели миниатюр для каждого активного окна в приложении (это значит, что в приложении имеется несколько окон, собранных вместе, и вы желаете использовать разные панели миниатюр для каждого из них);
- Как только кнопки назначены (как правило, к моменту, когда создана иконка в панели задач), вы более не можете добавлять или удалять кнопки из панели миниатюр; однако, при желании, можно кнопки скрыть.
Как обычно, начнем с реализации в неуправляемом коде (Win32). Данный пример вы найдете в Windows 7 SDK.
Чтобы добавить кнопку на панель миниатюр, необходимо использовать интерфейс ITaskbarList4, содержащий методы, которые позволят динамически добавлять, удалять и активировать элементы на панели задач. В частности, для работы с панелью миниатюр понадобятся следующие методы:
- ThumbBarAddButtons – добавляет панель миниатюр (панель, встроенная в миниатюру окна в панели задач) с указанным набором кнопок к изображению миниатюры окна на панели задач.
- ThumbBarSetImageList – определяет список изображений, который содержит изображения кнопок для панели задач, встроенной в изображение окна на панели задач.
- ThumbBarUpdateButtons – отображает, включает, отключает или скрывает кнопки в панели миниатюр в зависимости от текущего состоянием окна.
Как в случае с любой иной операцией, предполагающей взаимодействие с иконкой, вы должны убедиться, что у вас есть иконка панели задач; для этого необходимо зарегистрировать "TaskbarCreatedMessage" с помощью метода RegisterWindowMessage. Затем нужно дождаться сообщения для метода WndProc. Как только приложение получит это сообщение, вы можете без опаски идти вперед и создавать кнопки панели миниатюр. Этот же метод WndProc будет получать сообщение WM_COMMAND при нажатии любой из кнопок панели миниатюр, которые вы создали. Младшее слово параметра wParam содержит абсолютный идентификатор кнопки, определяющий, какая именно кнопка была нажата, в то время как старшее слово установлено в THBN_CLICKED. Следующий фрагмент кода иллюстрирует этот процесс:
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static UINT s_uTBBC = WM_NULL;
if (s_uTBBC == WM_NULL)
{
// Вычисляем значение для сообщения TaskbarButtonCreated
s_uTBBC = RegisterWindowMessage(L"TaskbarButtonCreated");
// Если приложение запущено с привилегиями администратора, разрешаем
// TaskbarButtonCreated and WM_COMMAND messages through.
ChangeWindowMessageFilter(s_uTBBC, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_COMMAND, MSGFLT_ADD);
}
if (message == s_uTBBC) //wmTaskbarButtonCreated)
{
// Как только получено сообщение TaskbarButtonCreated, мы можем создать
// панель миниатюр для окна.
CreateThumbnailToolbar(hWnd);
}
else switch (message)
{
case WM_COMMAND:
{
int const wmId = LOWORD(wParam);
switch (wmId)
{
case IDTB_BUTTON1:
case IDTB_BUTTON2:
case IDTB_BUTTON3:
{
WCHAR szMsg[100];
StringCchPrintf(
szMsg, ARRAYSIZE(szMsg),
L"Thumbnail toolbar button clicked,
ID=%d", wmId);
MessageBox(hWnd, szMsg, L"Application", MB_OK);
break;
}
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Обратите внимание, что сразу после регистрации на получение сообщения TaskbarCreatedMessage, вызывается метод ChangeWindowMessageFilter. Этот метод добавляет или удаляет сообщение из фильтра сообщений User Interface Privilege Isolation (UIPI). Это следует делать в том случае, если приложение выполняется с повышенными привилегиями. Одним словом, панель задач является частью процесса explorer.exe, которая выполняется при среднем уровне целостности, то есть от имени стандартного пользователя. Начиная с Windows Vista и включения в ее состав UIPI, приложения с более низким уровнем привилегий не смогут посылать сообщения Windows-приложениям более высокого уровня. Поэтому вы должны изменить фильтр, чтобы разрешить получение определенного типа сообщений от отправителей с более низким уровнем привилегий, в частности, речь идет о результатах TaskbarCreatedMessage и WM_COMMAND. Но это, пожалуй, послужит темой для отдельной статье об UIPI.
Возвращаясь к нашей теме, можно видеть, что WM_COMMAND обрабатывается очень простым способом, извлекая определенный идентификатор кнопки из младшего слова параметра wParam. На самом деле, с ним мы ничего не делаем (переключатель – один и тот же для всех кнопок); это всего лишь пример.
Теперь поговорим о том, как зарегистрировать панель миниатюр. В вышеприведенном фрагменте кода мы вызываем вспомогательный метод CreateThumbnailToolbar. Фрагмент кода будет выглядеть так:
HRESULT CreateThumbnailToolbar(HWND hWnd)
{
ITaskbarList4 *pTaskbarList;
HRESULT hr = CoCreateInstance(
CLSID_TaskbarList,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pTaskbarList));
if (SUCCEEDED(hr))
{
hr = pTaskbarList->HrInit();
if (SUCCEEDED(hr))
{
// Определяем, какое изображение использовать
// для кнопок панели миниатюр – наше решение
// основано на минимальном системном размере иконки.
// Это позволит сделать их дружелюбными к DPI.
struct
{
PCWSTR pbmp;
int cx;
}
const bitmaps[3] =
{
{ MAKEINTRESOURCE(IDB_BUTTONIMAGES_96), 16 },
{ MAKEINTRESOURCE(IDB_BUTTONIMAGES_120), 20 },
{ MAKEINTRESOURCE(IDB_BUTTONIMAGES_144), 24 }
};
int const cxButton = GetSystemMetrics(SM_CXSMICON);
int iButtons = 0;
for (int i = 0; i < ARRAYSIZE(bitmaps); i++)
{
if (bitmaps[i].cx <= cxButton)
{
iButtons = i;
}
}
HIMAGELIST himl = ImageList_LoadImage(
g_hInstance,
bitmaps[iButtons].pbmp,
bitmaps[iButtons].cx,
0,
RGB(255,0,255),
IMAGE_BITMAP,
LR_CREATEDIBSECTION);
if (himl)
{
hr = pTaskbarList->ThumbBarSetImageList(hWnd, himl);
if (SUCCEEDED(hr))
{
THUMBBUTTON buttons[3] = {};
// Первая кнопка
buttons[0].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
buttons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
buttons[0].iId = IDTB_BUTTON1;
buttons[0].iBitmap = 0;
StringCchCopy(buttons[0].szTip,
ARRAYSIZE(buttons[0].szTip), L"Button 1");
// Вторая кнопка
buttons[1].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
buttons[1].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
buttons[1].iId = IDTB_BUTTON2;
buttons[1].iBitmap = 1;
StringCchCopy(buttons[1].szTip,
ARRAYSIZE(buttons[1].szTip), L"Button 2");
// Третья кнопка
buttons[2].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
buttons[2].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
buttons[2].iId = IDTB_BUTTON3;
buttons[2].iBitmap = 2;
StringCchCopy(buttons[2].szTip,
ARRAYSIZE(buttons[2].szTip), L"Button 3");
// Размещаем кнопки на панели миниатюр
hr = pTaskbarList->ThumbBarAddButtons(
hWnd,
ARRAYSIZE(buttons),
buttons);
}
ImageList_Destroy(himl);
}
}
pTaskbarList->Release();
}
return hr;
}
Во-первых, необходимо создать COM-интерфейс ITasbarList4. Затем следует вызвать HrInit для инициализации объекта списка Taskbar. Этот метод должен быть вызван перед любыми другими вызываемыми методами ITaskbarList. Далее выбираем список изображений, с которым будем работать. Обратите внимание, что необходимо проверить ширину (в пикселях) маленькой иконки на соответствие рекомендуемому: настройки DPI могут изменить ее. И наконец, вызываем ThumbBarSetImageList, чтобы назначить иконки кнопкам. После загрузки исходного кода попытайтесь закомментировать эту строку и запустить приложение. Как вы думаете, что произойдет?
Далее, мы создаем массив из трех кнопок THUMBBUTTON и для каждой устанавливаем растровое изображение, всплывающую подсказку и флаги. Они используются для настройки кнопки панели миниатюр. Например, значение Bitmap указывает, что значение поля iBitmap (часть THUMBBUTTON) доступно. THB_FLAGS более интересен, поскольку его можно использовать для определения состояния и поведения кнопки – включена, выключена или скрыта. В SDK вы можете найти дополнительную информацию по теме. Мы также устанавливаем ID кнопки для того, чтобы различать нажатия на разные кнопки; этот идентификатор отправляется с сообщением WM_COMMAND.
Наконец, мы вызываем метод ThumbBarAddButtons, чтобы передать текущий параметр hWnd и массив кнопок. Он регистрирует панель миниатюр. Таким образом, в следующий раз, когда вы наведете курсор мыши на иконку приложения на панели задач, вы увидите три цветных кнопки как показано на следующем изображении.
Вы можете использовать Windows API Code Pack, чтобы создать панели миниатюр для приложения на управляемом коде. Собственно, об этом подробно рассказано в Windows 7 Training Kit, который доступен в учебном центре Channel 9. Демо-приложение, созданное Хайме Родригесом (Jaime Rodriguez), является WPF-приложением, которое демонстрирует все мельчайшие особенности новой панели задач. Мы лишь изучим лишь часть, касаемую панели миниатюр, остальное оставим для вашего самостоятельного изучения. Время от времени мы будем использовать это как основу для наших примеров на управляемом коде.
Как всегда, используя Windows API Code Pack, вам не стоит переживать о регистрации или обработке любых сообщений Windows. Только используйте TaskbarManager. В частности, если вы хотите работать с ThumbnailToolbars, который содержит метод AddButtons, который необходим для добавления объектов ThumbnailToolbarButton. ThumbnailToolbarButton представляет кнопку миниатюры панели задач, через которую вы можете включить или отключить кнопку панели задач, изменить ее значок или подсказку, и управлять ее видимостью. Вы можете также прикрепить обработчик событий к событию кнопки Click, что вполне соответствует ожиданиям разработчиков управляемого кода. Вы можете найти следующий фрагмент кода в файле ToolbarButtons.xaml.cs. Он показывает, насколько просто добавить панель миниатюр в приложение на управляемом коде:
private void CreateToolbarButtons()
{
buttonFirst = new ThumbnailToolbarButton(
TaskbarConcepts.Resources.first, "First Image");
buttonFirst.Enabled = false;
buttonFirst.Click += buttonFirst_Click;
buttonPrevious = new ThumbnailToolbarButton(
TaskbarConcepts.Resources.prevArrow, "Previous Image");
buttonPrevious.Enabled = false;
buttonPrevious.Click += buttonPrevious_Click;
buttonNext = new ThumbnailToolbarButton(
TaskbarConcepts.Resources.nextArrow, "Next Image");
buttonNext.Click += buttonNext_Click;
buttonLast = new ThumbnailToolbarButton(
TaskbarConcepts.Resources.last, "Last Image");
buttonLast.Click += buttonLast_Click;
TaskbarManager.Instance.ThumbnailToolbars.AddButtons
(
new WindowInteropHelper(Application.Current.MainWindow).Handle,
buttonFirst,
buttonPrevious,
buttonNext,
buttonLast
);
}
Остальная часть кода в этом файле отвечает за логику и обработку различных событий. Это дает возможность управлять различными изображениями, отображаемыми в главном приложении:
Надеюсь, что к настоящему моменту вы успели оценить преимущества включения панели миниатюр и поняли, что ее довольно просто реализовать.
Теперь попытаюсь объяснить, почему в панели миниатюр приложения Fishbowl шесть кнопок, а не пять (см. изображение выше). Если вы внимательно посмотрите на изображение, то обратите внимание, что четыре левых кнопки непосредственно влияют на поведение приложения, а единственная правая кнопка просто запускает браузер по умолчанию и автоматически загружает сайт Facebook. Поэтому имеет смысл использовать графический разграничитель между двумя наборами кнопок. Приложение создает шесть кнопок, но пятая кнопка скрыта, что создает иллюзию разделения кнопок. В Win32-приложениях для этого следует использовать ThumbBarUpdateButtons.
Разработчик управляемого кода увидит, что для сокрытия кнопки гораздо проще использовать ThubmnailButton. Тщательно изучив Windows API Code Pack, вы обнаружите, что класс ThubmnailButton имеет свойство Visible для управления видимостью конкретной кнопки панели. В конце раздела Visible Set вы найдете функцию UpdateThumbnailButton(), которая является оберткой для неуправляемого метода ThumbBarUpdateButtons.
Загрузить пример на неуправляемом коде (Win32) >>>
Загрузить пример на управляемом коде (WPF) >>>
Обучающие материалы по разработке для панели задач Windows 7 вы можете найти в учебном центре Channel 9.