Разработка для панели задач Windows 7 – переходим к спискам переходов – часть 2
Вторая часть статьи о списках перехода из серии, посвященной панели задач Windows 7. В предыдущей статье мы рассказали об элементах, которые входят в состав списков переходов: назначениях и задачах. Как разработчик вы имеете значительный уровень контроля над этими элементами. В этой статье мы поговорим о различных API, которые вы можете использовать при программировании списков переходов.
Сначала следует поговорить об одной важной вещи. Элементы, попадающие в категорию Recent (недавно открытых), также как и в любую другую, должны иметь зарегистрированный обработчик файлов для приложения в реестре. Это вовсе не означает, что приложение должно быть стандартным обработчиком этого определённого типа файлов. Это значит, что ваше приложение должно иметь зарегистрированный обработчик для всех файлов, которые вы хотите сделать доступными в выпадающем списке. Таким образом, элементами могут быть только файлы. Запомните, при щелчке по одному из элементов в выпадающем списке ОС исполняет команду, ассоциированную с этим файлом, по отношению к вашей программе. Когда вы регистрируете обработчик файлов, вы также указываете приложение, которое обрабатывает файл, и определяете, как обрабатывать входящие параметры. Еще одна важная вещь, которую необходимо помнить: все элементы (файлы) должны быть локальными, то есть находиться на локальном жёстком диске, и быть доступными вашему приложению. Таким образом, мы можем сказать, что каждый элемент в назначениях выпадающего списка является доступным локальным файлом с зарегистрированным обработчиком файлов.
Как мы упоминали в предыдущих статьях, как только вы зарегистрировали обработчик файлов, ОС фактически помогает отслеживать эти файлы. В следующей статье мы поговорим об обработчике файлов.
Шаг 1. Используйте стандартное поведение Windows «из коробки»
По умолчанию списки переходов содержат категорию «Recent», которая в случае использующих файлы приложений автоматически будет наполняться содержимым с помощью функции SHAddToRecentDocs. Эта функция добавляет использованный элемент (файл) в список оболочки недавно использованных документов. В дополнение к обновлению списка недавних документов оболочка добавляет ярлык в пользовательскую папку с одноименным названием. Панель задач Windows 7 использует этот список и папку недавних документов для заполнения списка последних документов в списке переходов.
Windows сможет выполнить работу за вас, если тип файла вашего приложения зарегистрирован в системе. Каждый раз, когда вы дважды щёлкаете на файле с зарегистрированным обработчиком до того, как Windows запустит приложение, ОС автоматически вызовет функцию SHAddToRecentDocs от имени вашего приложения. Это разместит элемент в списке недавних документов Windows, и, в конце концов, в категории «Recent» списка переходов. То же самое происходит при использовании общего файлового диалога (Common File Dialog, CFD) для открытия файлов с помощью типового приложения. Таким образом, это еще один хороший повод использовать CFD, дебютировавший в Windows Vista и играющий жизненно важную роль в отношении библиотек, как мы уже объясняли в одной из статей цикла.
Оба примера используют стандартное поведение Windows в тех случаях, когда у вас есть зарегистрированный обработчик и Application ID, благодаря которым файлы ассоциируются со списками недавно и часто используемых элементов. В обоих случаях Windows автоматически вставляет элемент в список переходов, только если вы специально не отключили эту возможность, используя COM API. Очевидно, что у пользователей также есть возможность удалить любой элемент из списка переходов. Удаляя элемент из списка, вы перемещаете его в список удалённых элементов, который мы обсудим ниже.
Шаг 2. Создайте собственную категорию
Если стандартные категории не отвечают вашим нуждам, то пришло время создать собственную категорию. Для этого необходимо использовать интерфейс ICustomDestinationList для создания собственного списка.
ICustomDestinationList открывает методы, которые позволяют приложению предоставлять собственные списки переходов с собственными назначениями и заданиями для их отображения в панели задач. Вот методы, которые мы используем для приведённого ниже примера:
- AppendCategory – определяет собственные категории и назначения для их включения в собственный список переходов
- AppendKnownCategory – определяет, что категории недавно и часто используемых документов должны быть включены в список переходов
- BeginList – инициирует начало сессии по созданию собственного списка перехода
- CommitList – декларирует, что список переходов, инициированный вызовом к BeginList, завершен и готов для отображения.
Следующий пример кода показывает, как создать новый собственный список под названием Custom Lists и добавить туда несколько элементов:
void CreateJumpList()
{
ICustomDestinationList *pcdl;
HRESULT hr = CoCreateInstance(
CLSID_DestinationList,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pcdl));
if (SUCCEEDED(hr))
{
//important to setup App Id for the Jump List
hr = pcdl->SetAppID(c_szAppID);
if (SUCCEEDED(hr))
{
UINT uMaxSlots;
IObjectArray *poaRemoved;
hr = pcdl->BeginList(
&uMaxSlots,
IID_PPV_ARGS(&poaRemoved));
if (SUCCEEDED(hr))
{
hr = _AddCategoryToList(pcdl, poaRemoved);
if (SUCCEEDED(hr))
{
pcdl->CommitList();
}
poaRemoved->Release();
}
}
}
}
Здесь можно видеть, что мы начали со стандартного вызова COM-инициализации. Мы вызвали CoCreateInstance для инициализации объекта ICustomDestinationList (вот в чем заключается удовольствие работы с COM). Дальше, мы указали Application ID, чтобы начать заполнять список элементами.
Функция BeginList инициировала начало сессии для пользовательского списка. Эта функция возвращает максимальное количество элементов, которое поместится в указанный список. Стандартное значение составляет 10 элементов. Вы могли заметить параметр элемента Remove, IObjectArray *poaRemoved, который BeginList() возвращает как выходной параметр. Он хранит любой элемент, удаленный пользователем из списка переходов в ходе текущей сессии.
Дальше мы вызывали вспомогательную функцию _AddCategoryToList(), чтобы провести всю фактическую работу по добавлению элементов в пользовательскую категорию.
// Это функция помощника, добавляющая элементы в коллекцию
// object HRESULT _AddCategoryToList(ICustomDestinationList *pcdl,
// IObjectArray *poaRemoved)
{
IObjectCollection *poc;
HRESULT hr = CoCreateInstance
(CLSID_EnumerableObjectCollection,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&poc));
if (SUCCEEDED(hr))
{
for (UINT i = 0; i < ARRAYSIZE(c_rgpszFiles); i++)
{
IShellItem *psi;
if (SUCCEEDED(SHCreateItemInKnownFolder(
FOLDERID_Documents,
KF_FLAG_DEFAULT,
c_rgpszFiles[i],
IID_PPV_ARGS(&psi)))
)
{
if(!_IsItemInArray(psi, poaRemoved))
{
poc->AddObject(psi);
}
psi->Release();
}
}
IObjectArray *poa;
hr = poc->QueryInterface(IID_PPV_ARGS(&poa));
if (SUCCEEDED(hr))
{
pcdl->AppendCategory(L"Custom category", poa);
poa->Release();
}
poc->Release();
}
return hr;
}
Еще одним новым интерфейсом, который мы использовали, является IObjectCollection, представляющий коллекцию объектов с поддержкой функции IUnknown. В нее мы добавили IShellItems. Каждый элемент (файл), который мы добавили в список переходов, относится к типу IShellItem. В вышеуказанном коде был создан объект элемента оболочки для одного файла, который существует в известной папке – Documents. Однако перед тем, как мы фактически добавили новый элемент в коллекцию, мы должны определить, удалил ли его пользователь. Если пользователь точно удалил элемент из списка, этот элемент будет в списке удалённых элементов (и ассоциированном с AppID) и, как разработчики, мы должны уважать желание пользователя и избегать добавления этого элемента в список переходов. У нас уже есть список удалённых элементов, IObjectArray *poaRemoved, который был получен при вызове функции BeginList(…) в момент инициализации нового списка.
На данном этапе у вас есть коллекция элементов оболочки, которые пользователь ожидает увидеть в списке переходов. Дальше мы добавим эту коллекцию в объект ICustomDestinationList и создадим новую категорию под названием «Custom category», pcdl->AppendCategory (L»Custom category», poa);.
Итак, мы успешно создали новую категорию в панели задач под названием «Custom category» и наполнили её четырьмя элементами. Однако наша работа еще не закончена. Финальный шаг функции CreateJumpList – вызвать CommitList(), чтобы закончить транзакцию, начатую с вызовом BeginList(). Только после нашего вызова CommitList() в списке будут отображаться новые элементы и категории. Вызов CommitList() запускает очищение списка удалённых элементов и создание нового списка удалённых элементов. Интерфейс ICustomDestinationList предоставляет «транзакционную базу» API.
Чтобы гарантировать удобство конечному пользователю, удостоверьтесь, что безопасная копия нового заполненного списка закончена и готова к использованию, и единственной операцией, которую должна выполнить панель задач – переключить указатель на новый список. Результат должен выглядеть следующим образом:
Используя Windows API Code Pack, мы можем создать то же самое приложение, используя управляемый код.
Как только мы убедились, что используем тот же AppID, что и все элементы панели задач, мы можем создать экземпляр списка переходов для кнопки, над которой работаем, как показано в следующем примере кода. Этот пример является частью CTOR основного окна приложения:
// Устанавливаем ID для приложения
Taskbar.AppId = appId;
// Получаем список переходов
jumpList = Taskbar.JumpList;
category1 = new CustomCategory("Custom Category 1");
category2 = new CustomCategory("Custom Category 2");
// Добавляем собственные категории
jumpList.CustomCategories.Add(category1);
jumpList.CustomCategories.Add(category2);
// Значения по умолчанию для списков переходов
comboBoxKnownCategoryType.SelectedItem = "Recent";
Здесь мы указали AppID, используя свойство AppId, и создали экземпляр списка переходов, используя статическое свойство Taskbar.JumpList. Мы также создали две категории под названием Custom Category 1 и Custom Category 2. Потом мы добавили эти категории в контейнер пользовательских категорий списков. И в конце мы установили категорию Known в Recent. Это автоматически заполнит список, как описывалось выше.
После того, как мы настроили пользовательскую категорию, самое время наполнить её определённым содержимым. Чтобы сделать это, нам просто необходимо вызвать функцию Add, чтобы добавить JumpListItem в JumpListCollection. JumpListItemCollection является общей коллекцией (принадлежащей <IJumpListItem>), содержащей элементы IJumpListItem. По своей сути, элемент IJumpListItem является своего рода обёрткой для встроенного IShellItem.
// Указываем путь к объекту оболочки
string path = String.Format("{0}\\test{1}.txt",
executableFolder,
category1.JumpListItems.Count);
// Добавляем объект в категорию
category1.JumpListItems.Add(new JumpListItem(path));
Сначала нам надо указать путь к файлу, который мы хотим включить в список перехода. Пожалуйста, не забывайте, что мы можем вызвать функцию Add только, если файл размещен на локальном диске и доступен пользователю. Вышеуказанный код (наряду с несколькими другими методами, о которых мы расскажем в будущих статьях) станут результатом диалогового окна панели задач, которое будет выглядеть следующим образом:
И, наконец, нам надо вызвать функцию Taskbar.JumpList.RefreshTaskbarList(). Так как у нас мы использовали встроенную реализацию списка переходов, мы должны подтвердить изменения, внесённые в список. Более пристальный взгляд на функцию Refresh (доступ к ней у вас есть в Code Pack API) открывает нам функцию AppendCustomCategories, которая добавляет любую пользовательскую категорию в пользовательский список переходов. В этой функции вы можете найти управляемые реализации кода, приведённого выше. Также она включает в себя вызов функции AppendCategory, которая является обёрткой для функции AppendCategory, показанной выше.
IObjectCollection categoryContent =
(IObjectCollection)new CEnumerableObjectCollection();
// Добавляем представление каждой ссылки к массиву объектов
foreach (IJumpListItem link in category.JumpListItems)
categoryContent.AddObject(link.GetShellRepresentation());
// Добавляем текущую категорию к списку назначений
HRESULT hr = customDestinationList.AppendCategory(
category.Name,
(IObjectArray)categoryContent);
Как видите, разобраться в возможностях панели задач в Windows 7 не так уж сложно. Windows автоматически сделает основную часть работы, а если вам необходимо создать собственную категорию, то сделать это очень просто.
В следующей статье мы расскажем, как добавить задание в список переходов и зарегистрировать обработчик файлов.