API и FreeBasic. (дочерние окна-ListView)
Данная информация адаптирована с языка СИ . Уж очень хорошо и подробно объяснено про этот контрол. Авторы этой статьи Александр Фролов, Григорий Фролов. Оригинал ЗДЕСЬ . Поскольку материал был ими же выложен в свободный доступ и я указал их авторство, я надеюсь братья Фроловы не будут в обиде. Очень хорошо, что даже по прошествии почти 20 лет , материал еще актуален! Конечно статья местами претерпела изменения из-за разницы языков (в сторону упрощения), но в целом большая часть и мысль авторов сохранена.
Многие приложения отображают информацию в виде таблиц или списков. Операционная система Microsoft Windows предоставляет в распоряжение программиста очень мощный встроенный орган управления List View .
Этот орган управления позволяет отображать списки в нескольких видах:
детальный многоколоночный отчет с возможностью изменения ширины столбцов и
сортировки по столбцам;
окно с пиктограммами стандартного размера,
аналогичного окну раскрытой папки;
окно с пиктограммами уменьшенного
размера;
простой список с пиктограммами.
Такой набор подходит для большинства случаев. Например, детальный многоколоночный отчет удобен для организации просмотра и редактирования содержимого баз данных. Окно с пиктограммами можно использовать для работы с графическими изображениями.
Список состоит из элементов, каждому из которых назначается пиктограмма, название (label) и дополнительные текстовые строки, которые могут отображаться в столбцах при использовании режима детального просмотра. Кроме того, приложение может отмечать состояние элемента с помощью изображений из дополнительного списка.
Создание органа управления List View
Для использования органа управления List View ваше приложение должно выполнить несколько действий:
- создать окно на базе предопределенного класса окна WC_LISTVIEW ;
- создать один, два или три списка изображений, которые будут использованы для идентификации строк или для отображения состояния строк (назначение этих списков мы рассмотрим позже);
- добавить изображения (пиктограммы) в созданные списки;
- подключить списки изображений к органу управления List View;
- вставить столбцы и установить их ширину;
- вставить элементы списка, указав данные, которые должны быть отображены в столбцах или определив, что эти данные будут предоставлены позже при обработке соответствующего извещения.
Теперь подробно о том, как выполнить перечисленные выше действия.
Создание окна List View
Окно органа управления List View создается функцией CreateWindowEx на базе класса окна WC_LISTVIEW , например так:
hwndList = CreateWindowEx(0, WC_LISTVIEW, "", _ WS_VISIBLE Or WS_CHILD Or WS_BORDER _ Or LVS_REPORT Or LVS_EDITLABELS,_ 0, 0, rc.right - rc.left, rc.bottom - rc.top,_ hWnd, Cast(HMENU,IDC_LISTVIEW), hInst, NULL)
Перед вызовом этой функции вы должны загрузить в память DLL-библиотеку
COMCTL32.DLL , для чего следует вызвать функцию InitCommonControls без
параметров. (прим. Для Windows VISTA и выше вызывать функцию
InitCommonControls не требуется)
В дополнение к обычным стилям окна, таким как WS_VISIBLE, WS_CHILD и WS_BORDER, необходимо указать специальные стили с префиксом имени LVS_, определяющие внешний вид и поведение органа управления List View. Как минимум, следует указать один из следующих четырех стилей: LVS_REPORT , LVS_ICON, LVS_SMALLICON или LVS_LIST.
Ниже мы привели краткое описание стилей окна List View.
Стиль | Описание |
---|---|
LVS_REPORT | Список отображается в виде детального отчета, состоящего из нескольких столбцов |
LVS_ICON | Список отображается в виде окна с пиктограммами стандартного размера |
LVS_SMALLICON | Список отображается в виде окна с пиктограммами уменьшенного размера |
LVS_LIST | Простой список с пиктограммами уменьшенного размера |
LVS_ALIGNLEFT | Используется вместе с LVS_ICON и LVS_SMALLICON. Если указан этот стиль, пиктограммы будут выровнены по левой границе |
LVS_ALIGNTOP | Используется вместе с LVS_ICON и LVS_SMALLICON. Если указан этот стиль, пиктограммы будут выровнены по верхней границе. Стиль LVS_ALIGNTOP используется по умолчанию |
LVS_AUTOARRANGE | Используется вместе с LVS_ICON и LVS_SMALLICON для выполнения автоматического размещения пиктограмм внутри окна органа управления List View |
LVS_NOSCROLL | Отключение возможности свертки содержимого окна органа управления List View |
LVS_EDITLABELS | Этот стиль позволяет пользователю редактировать название элемента списка. Если указан стиль LVS_EDITLABELS, приложение должно обрабатывать извещение LVN_ENDLABELEDIT, которое будет описано позже |
LVS_NOCOLUMNHEADER | Если указан стиль LVS_NOCOLUMNHEADER, в режиме детального просмотра не отображается заголовок столбцов, с помощью которого выполняется сортировка и изменение размера столбцов |
LVS_NOLABELWRAP | Подпись под пиктограммами отображается в одной строке |
LVS_OWNERDRAWFIXED | Стиль позволяет родительскому окну выполнить рисование содержимого списка. Для этого родительское окно должно обрабатывать сообщение WM_DRAWITEM |
LVS_SHAREIMAGELIST | Этот стиль предназначен для организации совместного использования списков изображений несколькими органами управления List View |
LVS_SHOWSELALWAYS | Выбранные элементы списка отображаются с выделением даже в том случае, когда орган управления List View не активен |
LVS_SINGLESEL | Пользователь может выделить в списке только один элемент (по умолчанию можно выделить сразу несколько элементов) |
LVS_SORTASCENDING | Выполнение сортировки текстовых строк элементов в прямом порядке |
LVS_SORTDESCENDING | То же, но в обратном порядке |
Как уже говорилось, стили LVS_REPORT, LVS_ICON, LVS_SMALLICON и LVS_LIST
определяют режим работы органа управления List View. При создании этого органа
управления вы должны указать только один из перечисленных стилей. Однако в
дальнейшем если появится необходимость изменить режим работы, это можно будет
легко сделать при помощи функций GetWindowLong и SetWindowLong .
Когда список отображается в виде окна со стандартными или уменьшенными пиктограммами, у вас есть возможность выбрать один из нескольких способов выравнивания пиктограмм во внутренней области этого окна. Для этого вы должны указать один из следующих стилей: LVS_ALIGNLEFT, LVS_ALIGNTOP или LVS_AUTOARRANGE.
По умолчанию (если не указан стиль LVS_ALIGNLEFT) пиктограммы выравниваются по верхней границе окна. Такое поведение соответствует стилю LVS_ALIGNTOP. Вы можете также выровнять пиктограммы по левой границе окна, задав стиль LVS_ALIGNLEFT. Разумеется, стили LVS_ALIGNTOP и LVS_ALIGNLEFT несовместимы, поэтому вы можете использовать только один из них.
Для пользователя будет удобнее, если при изменении размеров окна просмотра пиктограммы будут автоматически перемещаться таким образом, чтобы по возможности занимать всю полезную площадь окна. Вы можете организовать автоматическое размещение пиктограмм, указав стиль LVS_AUTOARRANGE. Без этого стиля пользователю придется работать с полосой просмотра (Scrollbar), что не всегда удобно.
Иногда необходимо заблокировать возможность изменения размеров окна просмотра, исключив стиль WS_BORDER. Например, вы можете создать окно просмотра фиксированного размера, расположенное в диалоговой панели. В этом случае вы не должны указывать стиль LVS_NOSCROLL, так как иначе пользователь не получит доступа ко всем элементам списка.
Списки изображений
Итак, вы создали окно органа управления List View, выбрав для него подходящий набор стилей. Теперь следует заняться наполнением этого окна изображениями и элементами списка. В зависимости от того, в каких режимах будет работать создаваемый вами орган управления List View, вы должны создать один, два или три списка из следующего набора: список стандартных пиктограмм; список пиктограмм уменьшенного размера; список пиктограмм состояния. Как выбрать нужные списки? Список стандартных пиктограмм нужен только в том случае, если вы будете использовать орган управления List View в режиме LVS_ICON . Список пиктограмм уменьшенного размера нужен для всех остальных режимов.Что же касается списка пиктограмм состояния (state image list), то он используется для выделения состояния отдельных элементов списка.
Для создания списка изображения вы должны вызвать функцию ImageList_Create :
ImageList_Create( _ cx As Integer , _ 'ширина изображения cy As Integer , _ 'высота изображения flags As Uinteger , _ 'тип изображения cInitial As Integer ,_ 'первоначальное количество изображений cGrow As Integer ) As HIMAGELIST 'количество изображений, на которое 'увеличится размер списка при добавлении новых изображений
Параметры cx и cy определяют, соответственно, ширину и высоту добавляемых
изображений. Если вы создаете список пиктограмм стандартного или уменьшенного
размера, для определения значения этих параметров следует использовать функцию
GetSystemMetrics , передав ей в первом случае параметры SM_CXICON и SM_CYICON ,
а во втором - параметры SM_CXSMICON и SM_CYSMICON .
GetSystemMetrics возвращает информацию относительно различных параметров в Windows. Большинство их имеет дело с размерами различных объектов(экрана, значков, курсоров, и т.д). Эта функция обеспечивает информацию относительно системы.Возвращаемое значение функции зависит от значения, указанного для nIndex. Имейте в виду, что все размеры (ширина и высота) измеряются в пикселах. Значения одного единственного параметра можете посмотреть здесь
Параметр flags определяет тип изображений, из которых состоит список. Вы можете создавать список из пиктограмм, аппаратно-зависимых битовых изображений DDB или аппаратно-независимых битовых изображений DIB. Приведем список возможных значений параметра flags:
Значение | Описание |
---|---|
ILC_COLOR4 | 4-битовое изображение DIB |
ILC_COLOR8 | 8-битовое изображение DIB |
ILC_COLOR16 | 16-битовое изображение DIB |
ILC_COLOR24 | 24-битовое изображение DIB |
ILC_COLOR32 | 32-битовое изображение DIB |
ILC_PALETTE | Используются цветовые палитры |
ILC_COLORDDB | Изображение DDB |
ILC_MASK | Использование маски. Изображение состоит из двух изображений, причем одно из них является монохромной маской. Такой формат имеют пиктограммы и курсоры |
При создании списка изображений из пиктограмм в SDK значение параметра
flags рекомендуется указывать как TRUE, однако логичнее указать значение
ILC_MASK. Параметр cInitial определяет размер блока памяти, выделяемого для
хранения изображений, так как этот размер зависит от количества изображений. При
добавлении в созданный список новых изображений сверх значения, указанного в
этом параметре, происходит динамическое изменение размера заказанного блока
памяти. Такое изменение выполняется в соответствии со значением параметра cGrow.
Функция ImageList_Create при нормальном завершении возвращает идентификатор
созданного списка изображений, который надо сохранить для дальнейшего
использования. В случае ошибки возвращается значение NULL. Ниже приведен пример
создания двух списков, в которые первоначально будет добавлено девять
изображений:
himlSmall = ImageList_Create( _ GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),_ ILC_MASK, 9, 1) himlLarge = ImageList_Create( _ GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),_ ILC_MASK, 9, 1)
Заполнение списков изображений
Мы только что создали списки изображений, однако пока они пустые и орган управления ListView ничего о них "не знает".
Для добавления изображений вы можете воспользоваться функцией ImageList_AddIcon :
ImageList_AddIcon( _ himl As HIMAGELIST ,_ 'идентификатор списка изображений hicon As HICON) As Integer 'идентификатор добавляемого изображения
Если вы добавляете в список пиктограммы, определенные в ресурсах
приложения, вызов функции ImageList_AddIcon можно выполнять в цикле, загружая
пиктограммы функцией LoadIcon :
For i = IDI_ICON1 To IDI_ICON9 hIcon = LoadIcon(hInst, MAKEINTRESOURCE(i)) ImageList_AddIcon(himlSmall, hIcon) ImageList_AddIcon(himlLarge, hIcon) Next
Заметьте, что после добавления пиктограммы не уничтожаются явным образом,
так как при необходимости система сделает это автоматически.
Подключение списков изображений к органу ListView
На данном этапе мы создали и заполнили списки изображений. Теперь их надо подключить к органу управления ListView, вызвав макрокоманду ListView_SetImageList :
ListView_SetImageList( _ hwnd As HWND , 'идентификатор окна органа List View himl As HIMAGELIST , 'идентификатор подключаемого списка iImageList As Integer ) As HIMAGELIST 'тип изображений в списке
Первые два параметра макрокоманды задают, соответственно, идентификатор
окна органа ListView и идентификатор подключаемого списка, который мы только что
создали и заполнили.
Параметр iImageList может иметь следующие значения:
Значение | Содержимое списка |
---|---|
LVSIL_NORMAL | Пиктограммы стандартного размера |
LVSIL_SMALL | Пиктограммы уменьшенного размера |
LVSIL_STATE | Пиктограммы состояния элементов списка |
Макрокоманда ListView_SetImageList посылает органу управления ListView
сообщение LVM_SETIMAGELIST и определена следующим образом:
#DEFINE ListView_SetImageList(w,h,i) _ Cast(HIMAGELIST,Cuint(SendMessage(w,LVM_SETIMAGELIST,i,Cint( h))))
Вы можете посылать сообщение LVM_SETIMAGELIST непосредственно, однако макрокомандой удобнее пользоваться. Вот соответствующий фрагмент исходного текста приложения, подключающий два списка:
ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL) ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL)
Вставка столбцов
Если вы собираетесь просматривать список в виде детального отчета (стиль LVS_REPORT), необходимо вставить нужное количество столбцов, а также определить для каждого столбца заголовок и ширину.
Вставка столбцов выполняется макрокомандой ListView_InsertColumn , посылающей органу управления сообщение LVM_INSERTCOLUMN :
ListView_InsertColumn( _ hwnd As HWND , _ 'идентификатор органа Listview iCol As Integer ,_'номер столбца pcol As LV_COLUMN Ptr )As Integer 'адрес структуры LV_COLUMN
Нумерация столбцов начинается с нуля.
Перед тем как вызывать макрокоманду ListView_InsertColumn, вы должны по очереди для каждого столбца заполнить структуру LV_COLUMN , определенную следующим образом:
Type LV_COLUMN As Uinteger mask ' маска использования полей структуры LV_COLUMN As Integer fmt ' тип выравнивания для столбца As Integer cx ' ширина столбца в пикселах As LPTSTR pszText ' адрес строки заголовка столбца As Integer cchTextMax ' размер буфера, адрес которого 'задан в pszText As Integer iSubItem ' номер дополнительного элемента End Type
Внимание! В заголовках (версии
0.23) присутствует ошибка. Вместо структур LV_COLUMN, LV_ITEM, LV_DISPINFO,
NM_LISTVIEW определены структуры LVCOLUMN, LVITEM , NMLVDISPINFO, NMLISTVIEW
соответственно.
Поле маски mask определяет, какие из полей структуры LV_COLUMN будут использованы. Ниже мы перечислили возможные значения масок, которые можно объединять при помощи логической операции ИЛИ:
Маска | Заполненное поле структуры LV_COLUMN |
---|---|
LVCF_FMT | fmt |
LVCF_SUBITEM | iSubItem |
LVCF_TEXT | pszText |
LVCF_WIDTH | cx |
Поле fmt определяет тип выравнивания для столбца. Можно указывать одно из
следующих значений: LVCFMT_LEFT, LVCFMT_RIGHT или LVCFMT_CENTER. Они задают,
соответственно, выравнивание влево, вправо или по центру столбца.
Ширина столбца в пикселах задается полем cx.
Для того чтобы задать заголовок столбца, вы должны определить буфер и записать в него соответствующую текстовую строку, закрыв ее двоичным нулем. Размер буфера должен быть указан в поле cchTextMax (в байтах).
Теперь о поле iSubItem.
Как мы уже говорили раньше, список содержит элементы, каждый из которых имеет имя и связанную с ним пиктограмму (привязка пиктограммы выполняется на этапе добавления строк). Кроме того, для каждого элемента можно задать дополнительные элементы. В SDK элементы называются item, а дополнительные элементы - subitem.
В режиме детального отчета пиктограмма и название элемента отображаются в самом левом столбце, имеющим номер 0. Дополнительные элементы, заданные в виде текстовых строк, отображаются в остальных столбцах детального отчета (с номерами 1, 2 и т. д.).
В поле iSubItem вы должны записать номер дополнительного элемента, который будет связан с текущим столбцом отчета.
Ниже приведен фрагмент кода, в котором выполняется вставка трех столбцов с номерами 0, 1 и 2:
GetClientRect(hWnd, @rc) memset(@lvc, 0, Sizeof(lvc)) lvc.mask = LVCF_FMT Or LVCF_WIDTH Or LVCF_TEXT Or LVCF_SUBITEM lvc.fmt = LVCFMT_LEFT lvc.cx = (rc.right - rc.left) / 4 lvc.iSubItem = 0 lvc.pszText = "Application Name" ListView_InsertColumn(hwndList, 0, @lvc) lvc.iSubItem = 1 lvc.pszText = "Icon Name" ListView_InsertColumn(hwndList, 1, @lvc) lvc.iSubItem = 2 lvc.pszText = "Cost, USD" ListView_InsertColumn(hwndList, 2, @lvc) ListView_SetColumnWidth(hwndList,2,(rc.right - rc.left) / 8)
Ширина первого и второго столбца устанавливается равной четверти ширины
главного окна приложения (которая в нашем случае равна ширине окна органа
управления List View).
Ширина третьего столбца устанавливается равной одной восьмой ширины окна, причем для разнообразия мы делаем это при помощи макрокоманды ListView_SetColumnWidth . Эта макрокоманда посылает органу List View сообщение LVM_SETCOLUMNWIDTH . Такую процедуру можно выполнять и позже, а не только в момент создания окна органа управления List View.
Вставка элементов списка
На последнем этапе вы должны вставить в список элементы. Проще всего это сделать с помощью макрокоманды ListView_InsertItem , посылающей органу управления List View сообщение LVM_INSERTITEM :
ListView_InsertItem( _ hwnd As HWND , _ 'идентификатор окна List View pitem As LV_ITEM Ptr) As Integer 'адрес структуры LV_ITEM
Из-за ошибок определения названий структур, использовать макрос ListView_InsertItem без переопределения нельзя. Поэтому в нашем коде мы будем использовать прямой вызов SendMessage.
Перед тем как вставлять элемент, вы должны записать его атрибуты в структуру LV_ITEM , определенную следующим образом:
Type LV_ITEM As Uinteger mask ' маска использования полей структуры LV_ITEM As Integer iItem ' номер элемента As Integer iSubItem ' номер дополнительного элемента As Uinteger state ' текущее состояние элемента As Uinteger stateMask ' маска состояния элемента As LPTSTR pszText ' адрес текстового буфера As Integer cchTextMax' размер текстового буфера As Integer iImage ' номер пиктограммы элемента As LPARAM lParam ' 32-битовое значение, связанное с элементом End Type
Поле маски mask определяет, какие из полей структуры LV_ITEM будут использованы при добавлении элементов. Возможны следующие значения масок (их можно объединять при помощи логической операции ИЛИ):
Маска | Заполненное поле структуры LV_COLUMN |
---|---|
LVIF_TEXT | pszText |
LVIF_IMAGE | iImage |
LVIF_PARAM | lParam |
LVIF_STATE | state |
Ниже приведен фрагмент исходного текста приложения List Application, в котором к созданному ранее списку добавляется 9 элементов:
' Вставляем строки lvi.mask = LVIF_IMAGE Or LVIF_TEXT Or LVIF_PARAM lvi.pszText = LPSTR_TEXTCALLBACK For i=0 To 8 lvi.iItem = i lvi.cchTextMax = 40 lvi.lParam = Cast(LPARAM,@rgApplInfo(i)) For j As Integer = 0 To 2 lvi.iImage = i lvi.iSubItem = j SendMessage(hwndList,LVM_INSERTITEM,0,Cast(LPARAM,@lvi)) Next Next
Так как в нашем примере пиктограммы состояния элементов не используются,
мы не заполняем поля state и stateMask.
В поле iItem мы записываем номер элемента, изменяя его в цикле от 0 до 9.
Аналогичным образом мы поступаем и с полем iImage, записывая в него номер пиктограммы для элемента. Напомним, что в разных режимах работы органа управления List View будут использованы пиктограммы из разных списков (стандартного и уменьшенного размера).
В поле pszText можно было бы записать адрес текстовой строки, соответствующей названию элемента или дополнительному элементу, однако мы занесли туда значение LPSTR_TEXTCALLBACK . В этом случае адрес нужной строки будет предоставляться при обработке соответствующего извещения, о чем речь еще впереди.
Обработка извещений
Приложение, создавшее орган управления List View, должно обрабатывать сообщение WM_NOTIFY , поступающее в функцию окна, создавшего этот орган управления.
Коды извещений
Код извещения передается через поле code структуры NMHDR . Напомним, что адрес этой структуры находится в параметре lParam сообщения WM_NOTIFY.
Родительское окно может получить следующие коды извещений:
Код извещения | Описание |
LVN_BEGINDRAG | Начало операции переноса "drag and drop" |
LVN_BEGINLABELEDIT | Начало операции редактирования названия элемента |
LVN_BEGINRDRAG | Начало операции переноса "drag and drop" с использованием правой клавиши мыши |
LVN_COLUMNCLICK | Пользователь сделал щелчок мышью по заголовку столбца в режиме детального отчета |
LVN_DELETEALLITEMS | Удаление всех элементов списка |
LVN_DELETEITEM | Удаление определенного элемента списка |
LVN_ENDLABELEDIT | Завершение операции редактирования названия элемента |
LVN_GETDISPINFO | Орган управления запрашивает информацию, необходимую для отображения элемента. Это извещение приходит, в частности, когда при добавлении элемента вместо адреса реальной текстовой строки была указана константа LPSTR_TEXTCALLBACK |
LVN_INSERTITEM | Вставка в список нового элемента |
LVN_ITEMCHANGED | Произошло изменение элемента |
LVN_ITEMCHANGING | С помощью этого извещения родительскому окну предоставляется возможность отменить предполагаемое изменение элемента |
LVN_KEYDOWN | Была нажата клавиша |
LVN_SETDISPINFO | Родительское окно должно обновить информацию об элементах списка, которую оно хранит в своих структурах данных |
Рассмотрим особенности обработки некоторых извещений на примере нашего
приложения List Application. Подробную информацию об остальных извещениях вы
сможете найти в справочной системе SDK.
LVN_GETDISPINFO
Так как при добавлении элементов в поле pszText была записана константа LPSTR_TEXTCALLBACK, для отображения списка орган управления List View "попросит" родительское окно предоставить ему адрес реальной текстовой строки. В результате родительское окно получит извещение с кодом LVN_GETDISPINFO.
Вместе с этим извещением в параметре lParam передается указатель на структуру LV_DISPINFO , которая будет использоваться для передачи информации:
Type LV_DISPINFO As NMHDR hdr ' для любого сообщения WM_NOTIFY As LV_ITEM item ' информация об элементе списка End Type
Структура LV_ITEM была описана выше.
При получении извещения LVN_GETDISPINFO родительское окно должно проверить содержимое поля mask структуры item. Маски в этом поле определяют, какая информация должна быть предоставлена при обработке извещения. Возможны следующие значения:
Значение | Запрашиваемая информация |
LVIF_IMAGE | Необходимо заполнить в структуре item поле iImage (номер изображения в списке изображений) |
LVIF_STATE | Поле state (состояние элемента списка) |
LVIF_TEXT | В поле pszText необходимо записать адрес буфера, содержащего строку текста |
Вот пример обработки извещения LVN_GETDISPINFO:
Dim As NMLVDISPINFO Ptr lpLvdi = Cast(NMLVDISPINFO Ptr,pnmhdr) Dim As APPLINFO Ptr lpAppinfo = Cast(APPLINFO Ptr,lpLvdi->item.lParam) Static As ZString*20 szBuf .... Case LVN_GETDISPINFO If lpLvdi->item.mask And LVIF_TEXT Then Select Case lpLvdi->item.iSubItem Case 0 lpLvdi->item.pszText = @lpAppinfo->szAppName Case 1 lpLvdi->item.pszText = @lpAppinfo->szIconName Case 2 szBuf=Str(lpAppinfo->iCost) lpLvdi->item.pszText = @szBuf End Select Endif
Если в поле mask установлен флаг LVIF_TEXT , обработчик извещения
анализирует поле iSubItem структуры item.
В том случае, когда содержимое этого поля равно 0, требуется получить текстовую строку названия элемента списка. Адрес этой строки определяется с помощью поля lParam структуры item (соответствующее значение было записано в это поле при добавлении элементов к списку макрокомандой ListView_InsertItem ).
Аналогично обрабатывается извещение для первого дополнительного элемента. Второй дополнительный элемент содержит не текстовое, а числовое значение. Поэтому нам приходится выполнять преобразование при помощи функции str.
LVN_COLUMNCLICK
Проверяя работу органа управления List View, вы сможете убедиться в том что с его помощью можно легко выполнить сортировку элементов списка по имени или по содержимому дополнительных элементов. Для этого вам достаточно лишь сделать щелчок мышью по заголовку нужного столбца.
Можно было бы подумать, что орган управления List View выполняет сортировку сам, без дополнительных усилий со стороны программиста, однако это не так. Очевидно, функции окна List View не известно, какой именно алгоритм сортировки должен быть использован в вашем приложении.
Тем не менее вы без особого труда сможете организовать простейшую сортировку текстовых и числовых полей. Для этого вам нужно создать обработчик извещения LVN_COLUMNCLICK и написать свою функцию сравнения двух элементов.
Обработчик извещения LVN_COLUMNCLICK может выглядеть, например, так:
Dim As NMLISTVIEW Ptr lpNm = Cast (NMLISTVIEW Ptr,pnmhdr) .... Case LVN_COLUMNCLICK ListView_SortItems(lpNm->hdr.hwndFrom,_ @LVCompareProc, Cast(LPARAM,lpNm->iSubItem)) ....
Обработка извещения LVN_COLUMNCLICK сводится к вызову единственной
макрокоманды ListView_SortItems, посылающей окну органа управления List View
сообщение LVM_SORTITEMS :
ListView_SortItems( _ hwnd As HWND , _ 'идентификатор окна органа List View pfnCompare As PFNLVCOMPARE , _' указатель на функцию сравнения lParamSort As LPARAM ) ' произвольное значение, которое ' передается функции сравнения
В качестве последнего параметра мы передаем макрокоманде номер
дополнительного элемента, по которому выполняется сортировка.
При вызове обработчика извещения LVN_COLUMNCLICK в параметре lParam сообщения WM_NOTIFY передается адрес структуры NM_LISTVIEW , определенной следующим образом:
Type NM_LISTVIEW As NMHDR hdr ' для любого сообщения WM_NOTIFY As Int iItem ' номер элемента списка As Int iSubItem ' номер дополнительного элемента списка As UINT uNewState ' новое состояние элемента As UINT uOldState ' старое состояние элемента As UINT uChanged ' изменившиеся атрибуты элемента As Point ptAction ' позиция, в котором произошло событие As LPARAM lParam ' дополнительное значение End Type
Наш обработчик извещения LVN_COLUMNCLICK получает номер дополнительного
элемента, по которому выполняется сортировка, из поля iSubItem структуры
NM_LISTVIEW.
Функция сравнения должна выглядеть так (имя функции может быть любым):
Function LVCompareProc(lParam1 As LPARAM ,lParam2 As LPARAM ,_ lParamSort As LPARAM )As Integer Dim As Integer iResult ........ Return iResult
Параметры lParam1 и lParam2 указывают на данные, относящиеся к
сравниваемым элементам. Параметр lParamSort содержит значение, которое было
передано через последний параметр макрокоманде ListView_SortItems. В нашем
случае это номер дополнительного элемента списка, по которому выполняется
сравнение.
Пример функции сравнения вы найдете ниже в примере.
LVN_BEGINLABELEDIT
LVN_ENDLABELEDIT
Если при создании органа управления List View был указан стиль LVS_EDITLABELS, пользователь сможет редактировать имена элементов списка.
Для этого достаточно выделить нужный элемент и сделать по нему щелчок левой клавишей мыши. Примерно через секунду появится окно редактирования имени "по месту". Такой способ пригоден в любом режиме работы органа управления List View.
Перед началом редактирования имени элемента списка родительское окно получает извещение LVN_BEGINLABELEDIT. Если обработчик этого сообщения возвратит ненулевое значение, редактирование будет заблокировано. Таким образом можно запретить пользователю редактировать названия некоторых элементов списка.
В простейшем случае обработчик извещения LVN_BEGINLABELEDIT и LVN_ENDLABELEDIT может выглядеть так:
Case LVN_BEGINLABELEDIT Case LVN_ENDLABELEDIT If((lpLvdi->item.iItem <> -1) And _ (lpLvdi->item.pszText <> NULL)) Then lstrcpy(lpAppinfo->szAppName, lpLvdi->item.pszText) Endif
Когда пользователь завершил редактирование имени элемента списка,
родительскому окну передается извещение LVN_ENDLABELEDIT. При этом параметр
lParam сообщения WM_NOTIFY содержит адрес структуры LV_DISPINFO.
Обработчик извещения LVN_ENDLABELEDIT может, например, обновить имя элемента в структуре данных, где это имя хранится.
Если пользователь отменил редактирование, в поле item.iItem будет находиться значение -1. В этом случае обновление выполнять не нужно.
Выбор из списка
Как правило, список нужен не только для просмотра, но и для того чтобы сделать из него выбор одного или нескольких элементов.
Прежде чем выбрать элементы из списка, пользователь вначале должен их выделить. Один элемент списка можно выделить, щелкнув по нему левой клавишей мыши. Для того чтобы выделить несколько элементов, можно дополнительно воспользоваться клавишами <Shift> и <Control>. В первом случае будет выделен непрерывный диапазон элементов, во втором - любой набор элементов (на обязательно расположенных рядом).
Если окно органа управления List View создано внутри диалоговой панели, после выделения нужных элементов пользователь может нажать кнопку, подтверждающую выбор. В том случае, когда выбирается только один элемент, можно предоставить возможность выбора с помощью двойного щелчка левой клавишей мыши по изображению пиктограммы или по строке названия нужного элемента.
В любом случае у программиста возникает необходимость организовать поиск в списке выделенных элементов. Это можно сделать с помощью макрокоманды ListView_GetNextItem , посылающей окну органа управления List View сообщение LVM_GETNEXTITEM :
ListView_GetNextItem( _ HWND hwnd _ ' идентификатор органа List View Int iStart _ ' номер элемента, с которого начинается поиск UINT flags) ' условие поиска
Параметр iStart может быть равен -1. В этом случае поиск продолжается до
тех пор, пока не будет найден первый элемент, удовлетворяющий условию поиска.
Если же задан конкретный номер элемента, с которого начинается поиск, то этот
элемент в поиске не участвует.
Условие поиска задается в виде флагов, определяющих геометрическое расположение элементов, участвующих в поиске, и флагов, определяющих состояние элемента.
В первом наборе определено пять флагов:
Флаг расположения | Расположение элементов, участвующих в поиске |
LVNI_ABOVE | Выше указанного |
LVNI_ALL | Поиск выполняется во всех элементах (это значение используется по умолчанию) |
LVNI_BELOW | Ниже указанного |
LVNI_TOLEFT | Слева от указанного |
LVNI_TORIGHT | Справа от указанного |
Приведем возможные значения флагов состояния:
Флаг состояния | Состояние элемента |
LVNI_CUT | LVIS_CUT отмечен для удаления и последующей вставки |
LVNI_DROPHILITED | LVIS_DROPHILITED выделен как целевой элемент для операции перемещения "drag and drop" |
LVNI_FOCUSED | LVIS_FOCUSED элемент имеет фокус ввода |
LVNI_SELECTED | LVIS_SELECTED элемент выделен |
Для поиска выделенных элементов вам нужно использовать флаг состояния LVNI_SELECTED и флаг расположения LVNI_ALL.
В нашем приложении List Application мы выполняем выбор элемента двойным щелчком левой клавишей мыши по пиктограмме или строке названия элемента.
Как это можно осуществить?
Извещение NM_DBLCLK
Когда пользователь делает двойной щелчок левой клавишей мыши внутри окна органа управления List View, родительское окно получает сообщение WM_NOTIFY с кодом извещения NM_DBLCLK. Обработчик этого извещения может определить номер выделенного элемента с помощью макрокоманды ListView_GetNextItem , как это сделано в приведенном ниже фрагменте кода:
Case NM_DBLCLK Dim As Integer index Dim As LVITEM lvi Dim As ZString*256 szBuf,szTempbuf szTempbuf="Selected item: " ' Определяем номер выделенного элемента index = ListView_GetNextItem(hwndList,_ -1, LVNI_ALL Or LVNI_SELECTED) If (index = -1) Then Exit Sub /' Подготавливаем структуру типа LV_ITEM для получения текстовой информации об элементах'/ lvi.mask = LVIF_TEXT lvi.pszText = @szBuf lvi.cchTextMax=256 lvi.iItem = index ' Получаем название элемента lvi.iSubItem = 0 ListView_GetItem(hwndList, @lvi) szTempbuf+=szBuf szBuf="" /' Получаем текстовую строку, связанную с первым и вторым дополнительным элементом '/ lvi.iSubItem = 1 ListView_GetItem(hwndList, @lvi) szTempbuf+=" : " szTempbuf+=szBuf lvi.iSubItem = 2 ListView_GetItem(hwndList, @lvi) szTempbuf+=" : $" szTempbuf+=szBuf /' Выводим на экран текстовые строки для выбранного элемента '/ MessageBox(0, szTempbuf, szAppName, MB_OK)
Если пользователь сделал двойной щелчок там, где нет пиктограммы или
строки названия элемента, макрокоманда ListView_GetNextItem вернет значение -1.
В этом случае обработчик извещения NM_DBLCLK завершает свою работу, так как
пользователь не выбрал ни одного элемента.
Если же выбор сделан, обработчик извещения получает текстовую информацию о выделенном элементе списка с помощью макрокоманды ListView_GetItem. Эта макрокоманда заполняет поля структуры LV_ITEM, отмеченные в поле mask. Макрокоманда вызывается несколько раз - для основного и всех дополнительных элементов.
Сообщения для органа управления List View
Приложение может посылать органу управления List View многочисленные сообщения. С помощью этих сообщений можно изменять некоторые режимы работы органа, получать информацию об элементах списка, добавлять и удалять элементы списка и т. д.
Ограниченный объем книги не позволяет подробно описать все параметры этих сообщений, поэтому мы приведем только краткий список сообщений. За дополнительной информацией обращайтесь к справочной системе SDK. Там же вы найдете описание макрокоманд, с помощью которых удобно посылать эти сообщения. Некоторые из этих макрокоманд мы уже использовали в предыдущих разделах.
Сообщение | Описание |
LVM_ARRANGE | Выравнивание пиктограмм в окне просмотра списка |
LVM_CREATEDRAGIMAGE | Создание изображения, необходимого для выполнения операции перемещения "drag and drop" (в нашей книге эта возможность органа управления List View не описана) |
LVM_DELETEALLITEMS | Удаление всех элементов из списка |
LVM_DELETECOLUMN | Удаление столбца из детального отчета |
LVM_DELETEITEM | Удаление конкретного элемента из списка |
LVM_EDITLABEL | Начать процесс редактирования имени элемента |
LVM_ENSUREVISIBLE | Размещение элементов в окне просмотра таким образом, чтобы они были видны полностью или по крайней мере частично. При необходимости добавляются полосы просмотра |
LVM_FINDITEM | Поиск элемента в списке по имени или по строке, соответствующей дополнительному элементу |
LVM_GETBKCOLOR | Определение фонового цвета окна List View |
LVM_GETCALLBACKMASK | Определение маски функций обратного вызова |
LVM_GETCOLUMN | Определение атрибутов столбца |
LVM_GETCOLUMNWIDTH | Определение ширины столбца |
LVM_GETCOUNTPERPAGE | Определение количества элементов, которые можно разместить в видимой части окна просмотра по вертикали в режиме списка или детального отчета |
LVM_GETEDITCONTROL | Определение идентификатора однострочного редактора текста EDIT, который применяется для редактирования названия элемента. Этот идентификатор может быть затем использован для изменения параметров редактора текста, например, для ограничения длины нового имени элемента |
LVM_GETIMAGELIST | Получение идентификатора списка изображений |
LVM_GETISEARCHSTRING | Получение инкрементальной строки поиска |
LVM_GETITEM | Получение всех или некоторых атрибутов элемента списка |
LVM_GETITEMCOUNT | Определение количества элементов в списке |
LVM_GETITEMPOSITION | Определение позиции элемента списка |
LVM_GETITEMRECT | Определение границ, занимаемых элементом в окне просмотра |
LVM_GETITEMSPACING | Определение расстояния между изображениями, соответствующими элементам списка |
LVM_GETITEMSTATE | Определение состояния элемента |
LVM_GETITEMTEXT | Получение имени элемента или текстовой строки, соответствующей заданному дополнительному элементу |
LVM_GETNEXTITEM | С помощью этого сообщения можно получить элемент, расположенный вблизи заданного (выше, ниже, правее или левее) |
LVM_GETORIGIN | Текущие координаты (view origin) окна органа управления List View |
LVM_GETSELECTEDCOUNT | Определение количества выделенных элементов списка |
LVM_GETSTRINGWIDTH | Определение ширины заданной текстовой строки, которая получится при использовании шрифта, выбранного для органа управления List View |
LVM_GETTEXTBKCOLOR | Определение цвета фона для текста в окне органа управления List View |
LVM_GETTEXTCOLOR | Определение цвета текста в окне органа управления List View |
LVM_GETTOPINDEX | Определение номера самого верхнего отображаемого элемента списка |
LVM_GETVIEWRECT | Определение координат воображаемого прямоугольника, ограничивающего изображение элемента списка при просмотре в режиме стандартных или уменьшенных пиктограмм |
LVM_HITTEST | Определение элемента, расположенного в данной позиции |
LVM_INSERTCOLUMN | Добавление столбца |
LVM_INSERTITEM | Добавление элемента |
LVM_REDRAWITEMS | Принудительная перерисовка элементов списка, заданных диапазоном номеров |
LVM_SCROLL | Свертка содержимого окна органа управления List View |
LVM_SETBKCOLOR | Установка фонового цвета окна List View |
LVM_SETCALLBACKMASK | Установка маски функций обратного вызова |
LVM_SETCOLUMN | Установка атрибутов столбца |
LVM_SETCOLUMNWIDTH | Установка ширины столбца |
LVM_SETIMAGELIST | Подключение списка изображений к органу управления List View |
LVM_SETITEM | Установка всех или некоторых атрибутов заданного элемента списка |
LVM_SETITEMCOUNT | Подготовка списка для добавления в него новых элементов (расширение списка) |
LVM_SETITEMPOSITION | Перемещение элемента в заданную позицию |
LVM_SETITEMPOSITION32 | Перемещение элемента в заданную позицию с использованием 32-разрядных координат |
LVM_SETITEMSTATE | Установка состояния элемента |
LVM_SETITEMTEXT | Установка названия элемента или текста, соответствующего заданному дополнительному элементу |
LVM_SETTEXTBKCOLOR | Установка цвета фона для текста в окне органа управления List View |
LVM_SETTEXTCOLOR | Установка цвета текста в окне органа управления List View |
LVM_SORTITEMS | Сортировка элементов списка с использованием заданной функции сравнения |
LVM_UPDATE | Обновление элемента списка |
Изменение режима просмотра списка
Просмотрев приведенный только что список сообщений, вы не найдете там сообщения, предназначенного для изменения режима просмотра списка. Таким образом, с помощью посылки сообщения приложение не может изменить внешний вид окна (например, перейти от режима просмотра детального отчета к просмотру списка в виде пиктограмм).
Тем не менее, данная задача выполнима.
С помощью пары функций GetWindowLong и SetWindowLong приложение может изменить стиль окна созданного ранее органа управления List View, что приведет к немедленному изменению режима просмотра. Эти функции были описаны в 13 томе "Библиотеки системного программиста" в разделе "Дополнительная память в структуре окна".
Ниже мы привели фрагмент обработчика сообщения от строки меню, с помощью которой пользователь может переключить орган управления в режим просмотра пиктограмм стандартного размера из любого другого режима:
Case ID_OPTIONS_ICONVIEW dwStyle = GetWindowLong(hwndList, GWL_STYLE) If((dwStyle And LVS_TYPEMASK) <> LVS_ICON) Then SetWindowLong(hwndList, GWL_STYLE, _ (dwStyle And Not LVS_TYPEMASK) Or LVS_ICON) Endif
Изменение стиля окна выполняется только в том случае, если режим
просмотра отличается от определяемого флагом LVS_ICON.
Аналогичным образом вы можете установить любой другой нужный вам режим просмотра, используя флаги LVS_SMALLICON, LVS_LIST и LVS_REPORT.
Приложение: (пример)
В качестве примера приведен исходный текст приложения List Application, отображающего список приложений из 15 тома "Библиотеки системного программиста", посвященного созданию систем мультимедиа для Microsoft Windows.
В режиме детального отчета для каждого приложения отображается его пиктограмма, название приложения, а также такие дополнительные элементы, как имя файла, содержащего приложение и стоимость в USD.
Нажимая заголовки столбцов, вы сможете отсортировать список по названию, по имени файла пиктограммы, или по цене. Ширина столбцов поддается регулировке. Причем, если сделать двойной щелчок левой клавишей мыши по разделителю заголовка столбцов, ширина столбца, расположенного слева от разделителя, станет такой, чтобы в столбце поместилась самая длинная текстовая строка.
Если из меню Options выбрать строку Icon view, режим просмотра списка изменится. С помощью строки Small icon view можно просматривать список в виде пиктограмм уменьшенного размера. И, наконец, выбрав из меню Options строку List view, вы можете перевести окно органа управления List View в режим просмотра простого списка.
В любом режиме просмотра вы сможете редактировать название приложения, выбрав его и затем сделав по имени щелчок левой клавишей мыши.
Если же вы сделаете двойной щелчок левой клавишей мыши по пиктограмме или названию любого приложения, на экране появится диалоговая панель, в которой отображаются атрибуты выбранного вами элемента списка.
Описание функций
Займемся описанием функций приложения List View. Так как раньше мы уже приводили отдельные фрагменты этих функций, ограничимся кратким описанием.
Глобальные переменные
Массив структур rgApplInfo предназначен для хранения элементов списка. Соответствующая структура имеет тип APPLINFO и определена следующим образом:
Type APPLINFO As ZString*40 szAppName 'название приложения As ZString*20 szIconName 'имя файла пиктограммы As UINT iCost 'стоимость приложения End Type
В переменной hInst хранится идентификатор приложения,
полученный функцией GetModuleHandle. Строчные массивы szAppName и
szAppTitle хранят, соответственно, имя и заголовок приложения.
Переменная hwndList нужна для хранения идентификатора созданного органа управления List View.
WinMain
Функция WinMain сохраняет идентификатор приложения и проверяет, не было ли это приложение запущено ранее. Если было, то активизируется окно работающего приложения.
Далее функция регистрирует класс главного окна приложения, создает и отображает это окно, а затем запускает обычный цикл обработки сообщений.
WndProc
В задачу функции WndProc входит обработка следующих сообщений: WM_CREATE, WM_DESTROY, WM_COMMAND, WM_NOTIFY, WM_SIZE.
WndProc_OnCreate
Эта функция обрабатывает сообщение WM_CREATE, создавая и инициализируя орган управления List View. Размеры окна органа управления устанавливаются равными размерам внутренней области главного окна приложения и в дальнейшем изменяются соответствующим образом обработчиком сообщения WM_SIZE.
WndProc_OnDestroy
Функция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она уничтожает окно органа управления List View и останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage.
WndProc_OnCommand
Эта функция обрабатывает сообщение WM-COMMAND, поступающее от главного меню приложения. Строки временного меню Options (Icon view, Small icon view, List view и Report view) имеют идентификаторы, соответственно, ID_OPTIONS_ICONVIEW, ID_OPTIONS_SMALLICONVIEW, ID_OPTIONS_LISTVIEW и ID_OPTIONS_REPORTVIEW. Обработчики изменяют режим отображения списка с помощью функций GetWindowLong и SetWindowLong, как это было описано раньше.
WndProc_OnNotify
Функция WndProc_OnNotify обрабатывает сообщение WM_NOTIFY, поступающее от органа управления List View.
Процедура обработки извещений была описана ранее. Отметим только, что из-за того что различные извещения используют разный формат структуры данных, адрес которой передается через параметр lParam сообщения WM_NOTIFY, мы выполняем преобразование указателя NMHDR* pnmhdr к типам LV_DISPINFO и NM_LISTVIEW.
Кроме того, функция WndProc_OnNotify проверяет параметр idFrom чтобы убедиться в том, что извещение пришло именно от органа управления List View с идентификатором IDC_LISTVIEW.
WndProc_OnSize
Для того чтобы размеры органа управления List View всегда соответствовали размерам внутренней области главного окна приложения List Application, обработчик сообщения WM_SIZE, расположенный в функции WndProc_OnSize, изменяет размеры окна органа управления. Изменения выполняются функцией MoveWindow.
LVCompareProc
Функция LVCompareProc нужна для сортировки элементов списка. Она выполняет сравнение двух элементов списка. Через первые два параметра передаются адреса структур данных, соответствующих сравниваемым элементам. Последний параметр содержит номер дополнительного элемента или нуль, если сравниваются названия элементов.
Текстовые строки сравниваются при помощи функции strcmpi. Для сравнения численных значений мы использовали обычную операцию вычитания.
И наконец сам пример:
Файл List.bas
#INCLUDE "windows.bi" #INCLUDE "win/commctrl.bi" #INCLUDE "list.bi" Type APPLINFO As ZString*40 szAppName As ZString*20 szIconName As UINT iCost End Type ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Декларации функций ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Declare Function WndProc(hWnd As HWND ,msg As UINT ,wParam As WPARAM ,lParam As LPARAM ) As Integer Declare Function LVCompareProc(lParam1 As LPARAM ,lParam2 As LPARAM ,_ lParamSort As LPARAM )As Integer Declare Sub WndProc_OnCreate(hwnd As HWND) Declare Sub WndProc_OnDestroy(Hwnd As HWND) Declare Sub WndProc_OnCommand(hWnd As HWND ,id As Integer ) Declare Sub WndProc_OnSize(cx As Integer ,cy As Integer ) Declare Sub WndProc_OnNotify(idFrom As Integer ,pnmhdr As NMHDR Ptr ) Declare Sub WinMain() ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''' Глобальные переменные'''''''''''''''''''''''''' Dim Shared As APPLINFO rgApplInfo(8) = { _ ("Generic", "appicon.ico ", 5), _ ("Book", "book1.ico ", 2), _ ("Driver List", "drvlist.ico ", 22), _ ("MCI CD Player", "mcicdpl.ico ", 345), _ ("MCI String Player", "mcistrvw.ico", 54), _ ("MCI Wave Player", "mciwaver.ico", 32), _ ("MCI Window Demo", "mciwnd.ico ", 0), _ ("Sound Play", "sndplay.ico ", 0), _ ("Wave Play", "wave.ico ", 4)} Dim Shared As HINSTANCE hInst Dim Shared As ZString*40 szAppName="ListApp",szAppTitle = "List Application" Dim Shared As HWND hwndList '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''' Вход в программу'''''''''''' WinMain() '''''''''''''''''''''''''''''''''' ' ----------------------------------------------------- ' Функция WinMain ' ----------------------------------------------------- Sub WinMain() Dim As WNDCLASSEX wc Dim As HWND hWnd Dim As MSG msg Dim As Integer wScreen,hScreen hInst = GetModuleHandle(0) ' Проверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL) If hWnd<>0 Then If IsIconic(hWnd)<>0 Then ShowWindow(hWnd, SW_RESTORE) SetForegroundWindow(hWnd) Exit Sub Endif ' Регистрируем класс окна wc.cbSize = Sizeof(WNDCLASSEX) wc.hIconSm = LoadImage(hInst, _ MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0) wc.style = 0 wc.lpfnWndProc = Cast(WndProc,@WndProc) wc.cbClsExtra = 0 wc.cbWndExtra = 0 wc.hInstance = hInst wc.hIcon = LoadImage(hInst,_ MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0) wc.hCursor = LoadCursor(NULL, IDC_ARROW) wc.hbrBackground = Cast(HBRUSH,COLOR_WINDOW + 1) wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU) wc.lpszClassName = Strptr(szAppName) If RegisterClassEx(@wc)=0 Then Exit Sub Endif ' Узнаем размеры экрана wScreen = GetSystemMetrics(SM_CXSCREEN) hScreen = GetSystemMetrics(SM_CYSCREEN) ' Создаем главное окно приложения hWnd = CreateWindow(szAppName,szAppTitle,WS_OVERLAPPEDWINDOW Or WS_VISIBLE,_ (wScreen-500)\2,(hScreen-500)\2,500,500, NULL, NULL, hInst, NULL) If hWnd=0 Then Exit Sub 'Запускаем цикл обработки сообщений While GetMessage(@msg,0,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend End Sub ' ----------------------------------------------------- ' Функция WndProc ' ----------------------------------------------------- Function WndProc(hWnd As HWND ,msg As UINT ,wParam As WPARAM ,lParam As LPARAM ) As Integer Select Case msg Case WM_CREATE WndProc_OnCreate(hwnd) Case WM_DESTROY WndProc_OnDestroy(hwnd) Case WM_COMMAND WndProc_OnCommand(hwnd,Loword(wParam)) Case WM_NOTIFY WndProc_OnNotify(Cast(Integer,wparam) , Cast(NMHDR Ptr,lParam)) Case WM_SIZE WndProc_OnSize(Loword(lParam),Hiword(lParam)) End Select Return DefWindowProc(hWnd, msg, wParam, lParam) End Function ' ----------------------------------------------------- ' Функция WndProc_OnCreate ' ----------------------------------------------------- Sub WndProc_OnCreate(hwnd As HWND) Dim As Integer i Dim As RECT rc Dim As HIMAGELIST himlSmall,himlLarge Dim As HICON hIcon Dim As LVCOLUMN lvc Dim As LVITEM lvi ' Определяем размеры внутренней области главного окна GetClientRect(hWnd, @rc) ' Инициализируем библиотеку стандартных органов управления InitCommonControls() ' Создаем орган управления List View hwndList = CreateWindowEx(0, WC_LISTVIEW, "", _ WS_VISIBLE Or WS_CHILD Or WS_BORDER Or LVS_REPORT Or _ LVS_EDITLABELS,0, 0, rc.right - rc.left, rc.bottom - rc.top, _ hWnd, Cast(HMENU,IDC_LISTVIEW) , hInst, NULL) If hwndList = NULL Then Exit Sub ' Создаем список изображений himlSmall = ImageList_Create( _ GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),_ ILC_COLOR32 , 9, 1) himlLarge = ImageList_Create( _ GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),_ ILC_COLOR32 , 9, 1) For i = IDI_ICON1 To IDI_ICON9 hIcon = LoadIcon(hInst, MAKEINTRESOURCE(i)) ImageList_AddIcon(himlSmall, hIcon) ImageList_AddIcon(himlLarge, hIcon) DestroyIcon(hicon) Next ' Добавляем списки изображений ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL) ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL) ' Вставляем столбцы lvc.mask = LVCF_FMT Or LVCF_WIDTH Or LVCF_TEXT Or LVCF_SUBITEM lvc.fmt = LVCFMT_LEFT lvc.cx = (rc.right - rc.left) / 4 lvc.iSubItem = 0 lvc.pszText = Strptr("Application Name") ListView_InsertColumn(hwndList, 0, @lvc) lvc.iSubItem = 1 lvc.pszText = Strptr("Icon Name") ListView_InsertColumn(hwndList, 1, @lvc) lvc.iSubItem = 2 lvc.pszText = Strptr("Cost, USD") ListView_InsertColumn(hwndList, 2, @lvc) ListView_SetColumnWidth(hwndList,2,(rc.right-rc.left) / 8) ' Вставляем строки lvi.mask = LVIF_IMAGE Or LVIF_TEXT Or LVIF_PARAM lvi.pszText = LPSTR_TEXTCALLBACK For i=0 To 8 lvi.iItem = i lvi.cchTextMax = 40 lvi.lParam = Cast(LPARAM,@rgApplInfo(i)) For j As Integer = 0 To 2 lvi.iImage = i lvi.iSubItem = j SendMessage(hwndList,LVM_INSERTITEM,0,Cast(LPARAM,@lvi)) Next Next End Sub ' ----------------------------------------------------- ' Функция WndProc_OnDestroy ' ----------------------------------------------------- Sub WndProc_OnDestroy(Hwnd As HWND) DestroyWindow(hwndList) PostQuitMessage(0) End Sub ' ----------------------------------------------------- ' Функция WndProc_OnCommand ' ----------------------------------------------------- Sub WndProc_OnCommand(hWnd As HWND ,id As Integer ) Dim As DWORD dwStyle Select Case id Case ID_OPTIONS_ICONVIEW dwStyle = GetWindowLong(hwndList, GWL_STYLE) If((dwStyle And LVS_TYPEMASK) <> LVS_ICON) Then SetWindowLong(hwndList, GWL_STYLE, _ (dwStyle And Not LVS_TYPEMASK) Or LVS_ICON) Endif Case ID_OPTIONS_SMALLICONVIEW dwStyle = GetWindowLong(hwndList, GWL_STYLE) If((dwStyle And LVS_TYPEMASK) <> LVS_SMALLICON)Then SetWindowLong(hwndList, GWL_STYLE,_ (dwStyle And Not LVS_TYPEMASK) Or LVS_SMALLICON) Endif Case ID_OPTIONS_LISTVIEW dwStyle = GetWindowLong(hwndList, GWL_STYLE) If((dwStyle And LVS_TYPEMASK) <> LVS_LIST)Then SetWindowLong(hwndList, GWL_STYLE,_ (dwStyle And Not LVS_TYPEMASK) Or LVS_LIST) Endif Case ID_OPTIONS_REPORTVIEW dwStyle = GetWindowLong(hwndList, GWL_STYLE) If((dwStyle And LVS_TYPEMASK) <> LVS_REPORT)Then SetWindowLong(hwndList, GWL_STYLE,_ (dwStyle And Not LVS_TYPEMASK) Or LVS_REPORT) Endif Case ID_FILE_EXIT WndProc_OnDestroy(hWnd) Case ID_HELP_ABOUT End Select End Sub '----------------------------------------------------- ' Функция WndProc_OnNotify '----------------------------------------------------- Sub WndProc_OnNotify(idFrom As Integer ,pnmhdr As NMHDR Ptr ) Dim As NMLVDISPINFO Ptr lpLvdi = Cast(NMLVDISPINFO Ptr,pnmhdr) Dim As APPLINFO Ptr lpAppinfo = Cast(APPLINFO Ptr,lpLvdi->item.lParam) Static As ZString*20 szBuf Dim As NMLISTVIEW Ptr lpNm = Cast (NMLISTVIEW Ptr,pnmhdr) If (idFrom <> IDC_LISTVIEW) Then Exit Sub Select Case pnmhdr->code Case LVN_GETDISPINFO If lpLvdi->item.mask And LVIF_TEXT Then Select Case lpLvdi->item.iSubItem Case 0 lpLvdi->item.pszText = @lpAppinfo->szAppName Case 1 lpLvdi->item.pszText = @lpAppinfo->szIconName Case 2 szBuf=Str(lpAppinfo->iCost) lpLvdi->item.pszText = @szBuf End Select Endif Case LVN_COLUMNCLICK ListView_SortItems(lpNm->hdr.hwndFrom,_ @LVCompareProc, Cast(LPARAM,lpNm->iSubItem)) Case LVN_BEGINLABELEDIT Case LVN_ENDLABELEDIT If((lpLvdi->item.iItem <> -1) And _ (lpLvdi->item.pszText <> NULL)) Then lstrcpy(lpAppinfo->szAppName, lpLvdi->item.pszText) Endif Case NM_DBLCLK Dim As Integer index Dim As LVITEM lvi Dim As ZString*256 szBuf,szTempbuf szTempbuf="Selected item: " ' Определяем номер выделенного элемента index = ListView_GetNextItem(hwndList,_ -1, LVNI_ALL Or LVNI_SELECTED) If (index = -1) Then Exit Sub /' Подготавливаем структуру типа LV_ITEM для получения текстовой информации об элементах'/ lvi.mask = LVIF_TEXT lvi.pszText = @szBuf lvi.cchTextMax=256 lvi.iItem = index ' Получаем название элемента lvi.iSubItem = 0 ListView_GetItem(hwndList, @lvi) szTempbuf+=szBuf szBuf="" /' Получаем текстовую строку, связанную с первым и вторым дополнительным элементом '/ lvi.iSubItem = 1 ListView_GetItem(hwndList, @lvi) szTempbuf+=" : " szTempbuf+=szBuf lvi.iSubItem = 2 ListView_GetItem(hwndList, @lvi) szTempbuf+=" : $" szTempbuf+=szBuf /' Выводим на экран текстовые строки для выбранного элемента '/ MessageBox(0, szTempbuf, szAppName, MB_OK) End Select End Sub ' ----------------------------------------------------- ' Функция WndProc_OnSize ' ----------------------------------------------------- Sub WndProc_OnSize(cx As Integer ,cy As Integer ) MoveWindow(hwndList, 0, 0, cx, cy, TRUE) End Sub ' ----------------------------------------------------- ' Функция LVCompareProc ' ----------------------------------------------------- Function LVCompareProc(lParam1 As LPARAM ,lParam2 As LPARAM ,_ lParamSort As LPARAM )As Integer Dim As APPLINFO Ptr pAppInfo1 = Cast(APPLINFO Ptr,lParam1) Dim As APPLINFO Ptr pAppInfo2 = Cast(APPLINFO Ptr,lParam2) Dim As LPSTR lpStr1, lpStr2 Dim As Integer iResult If pAppInfo1<>0 And pAppInfo2<>0 Then Select Case lParamSort Case 0 lpStr1 = @pAppInfo1->szAppName lpStr2 = @pAppInfo2->szAppName iResult = lstrcmpi(lpStr1, lpStr2) Case 1 lpStr1 = @pAppInfo1->szIconName lpStr2 = @pAppInfo2->szIconName iResult = lstrcmpi(lpStr1, lpStr2) Case 2 iResult = pAppInfo1->iCost - pAppInfo2->iCost End Select Endif Return iResult End Function
Файл List.bi
#DEFINE IDR_APPMENU 102 #DEFINE IDI_APPICON 103 #DEFINE IDI_APPICONSM 104 #DEFINE IDI_ICON1 105 #DEFINE IDI_ICON2 106 #DEFINE IDI_ICON3 107 #DEFINE IDI_ICON4 108 #DEFINE IDI_ICON5 109 #DEFINE IDI_ICON6 110 #DEFINE IDI_ICON7 111 #DEFINE IDI_ICON8 112 #DEFINE IDI_ICON9 113 #DEFINE ID_FILE_EXIT 40001 #DEFINE ID_HELP_ABOUTLISTAPPLICATION 40002 #DEFINE ID_HELP_ABOUT 40003 #DEFINE ID_OPTIONS_ICONVIEW 40004 #DEFINE ID_OPTIONS_SMALLICONVIEW 40005 #DEFINE ID_OPTIONS_LISTVIEW 40006 #DEFINE ID_OPTIONS_REPORTVIEW 40007 #DEFINE IDC_LISTVIEW 1234 #IFDEF APSTUDIO_INVOKED #IFNDEF APSTUDIO_READONLY_SYMBOLS #DEFINE _APS_NEXT_RESOURCE_VALUE 115 #DEFINE _APS_NEXT_COMMAND_VALUE 40008 #DEFINE _APS_NEXT_CONTROL_VALUE 1000 #DEFINE _APS_NEXT_SYMED_VALUE 101 #ENDIF #ENDIF
Файл List.rc
//Microsoft Visual C++ generated resource script. #INCLUDE "list.bi" ////////////////////////////////////////////////////////////// // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Options" BEGIN MENUITEM "&Icon view", ID_OPTIONS_ICONVIEW MENUITEM "&Small icon view", ID_OPTIONS_SMALLICONVIEW MENUITEM "&List view", ID_OPTIONS_LISTVIEW MENUITEM "&Report view", ID_OPTIONS_REPORTVIEW END POPUP "&Help" BEGIN MENUITEM "&About List Application...", ID_HELP_ABOUT END END ////////////////////////////////////////////////////////////// // Icon // IDI_APPICON ICON DISCARDABLE "list.ico" IDI_APPICONSM ICON DISCARDABLE "listsm.ico" IDI_ICON1 ICON DISCARDABLE "appicon.ico" IDI_ICON2 ICON DISCARDABLE "book1.ico" IDI_ICON3 ICON DISCARDABLE "drvlist.ico" IDI_ICON4 ICON DISCARDABLE "mcicdpl.ico" IDI_ICON5 ICON DISCARDABLE "mcistrwv.ico" IDI_ICON6 ICON DISCARDABLE "mciwaver.ico" IDI_ICON7 ICON DISCARDABLE "mciwnd.ico" IDI_ICON8 ICON DISCARDABLE "sndplay.ico" IDI_ICON9 ICON DISCARDABLE "wave.ico" ////////////////////////////////////////////////////////////// // String Table // STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Quits the application" ID_HELP_ABOUTLISTAPPLICATION "Displays program information and copyright" ID_OPTIONS_ICONVIEW "Each item appears as a full-sized icon" ID_OPTIONS_SMALLICONVIEW "Each item appears as a small icon" ID_OPTIONS_LISTVIEW "Each item appears as a small icon arranged in columns" ID_OPTIONS_REPORTVIEW "Each item appears with subitems arranged in columns" END
Рисунок того, что в итоге получается:
И конечно скачать весь архив с проектом:
содержание | назад | вперед