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

Рисунок того, что в итоге получается:

listaplication.png

И конечно скачать весь архив с проектом:

Скачать

содержание | назад | вперед