Операционная система Windows 95 для программиста© Александр Фролов, Григорий ФроловТом 22, М.: Диалог-МИФИ, 1993, 271 стр. 3.5. Приложение List ApplicationВ качестве примера мы приведем исходные тексты приложения List Application, отображающего список приложений из 15 тома "Библиотеки системного программиста", посвященного созданию систем мультимедиа для Microsoft Windows. В режиме детального отчета (рис. 3.1) для каждого приложения отображается его пиктограмма, название приложения, а также такие дополнительные элементы, как имя файла, содержащего приложение и стоимость в USD (не подумайте только, что мы и в самом деле продаем их по такой бешеной цене!).
Рис. 3.1. Просмотр списка в виде детального отчета Нажимая заголовки столбцов, вы сможете отсортировать список по названию, по имени файла пиктограммы, или по цене. Ширина столбцов поддается регулировке. Причем, если сделать двойной щелчок левой клавишей мыши по разделителю заголовка столбцов, ширина столбца, расположенного слева от разделителя, станет такой, чтобы в столбце поместилась самая длинная текстовая строка. Если из меню Options выбрать строку Icon view, режим просмотра списка изменится (рис. 3.2).
Рис. 3.2. Просмотр списка в виде пиктограмм стандартного размера С помощью строки Small icon view можно просматривать список в виде пиктограмм уменьшенного размера (рис. 3.3).
Рис. 3.3. Просмотр списка в виде пиктограмм уменьшенного размера И, наконец, выбрав из меню Options строку List view, вы можете перевести окно органа управления List View в режим просмотра простого списка (рис. 3.4).
Рис. 3.4. Простой список с пиктограммами уменьшенного размера В любом режиме просмотра вы сможете редактировать название приложения, выбрав его и затем сделав по имени щелчок левой клавишей мыши. Если же вы сделаете двойной щелчок левой клавишей мыши по пиктограмме или названию любого приложения, на экране появится диалоговая панель, в которой отображаются атрибуты выбранного вами элемента списка (рис. 3.5).
Рис. 3.5. Отображение информации, связанной с выбранным элементом списка Исходные тексты приложения List ApplicationВсе функции приложения List Application определены в файле list.c (листинг 3.1). Листинг 3.1. Файл list\list.c #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "afxres.h" #include "list.h" typedef struct tagAPPLINFO { char szAppName[40]; char szIconName[20]; UINT iCost; } APPLINFO; // ----------------------------------------------------- // Глобальные переменные // ----------------------------------------------------- APPLINFO rgApplInfo[]= { {"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} }; HINSTANCE hInst; char szAppName[] = "ListApp"; char szAppTitle[] = "List Application"; HWND hwndList; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (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 = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName,szAppTitle,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,0,CW_USEDEFAULT,0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand); HANDLE_MSG(hWnd, WM_NOTIFY, WndProc_OnNotify); HANDLE_MSG(hWnd, WM_SIZE, WndProc_OnSize); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { int i; RECT rc; HIMAGELIST himlSmall; HIMAGELIST himlLarge; HICON hIcon; LV_COLUMN lvc; LV_ITEM lvi; // Определяем размеры внутренней области главного окна GetClientRect(hWnd, &rc); // Инициализируем библиотеку стандартных органов управления InitCommonControls(); // Создаем орган управления List View hwndList = CreateWindowEx(0L, WC_LISTVIEW, "", WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT | LVS_EDITLABELS, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hWnd, (HMENU) IDC_LISTVIEW, hInst, NULL); if(hwndList == NULL) return FALSE; // Создаем список изображений 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); for(i = IDI_ICON1; i <= IDI_ICON9; i++) { hIcon = LoadIcon(hInst, MAKEINTRESOURCE(i)); ImageList_AddIcon(himlSmall, hIcon); ImageList_AddIcon(himlLarge, hIcon); } // Добавляем списки изображений ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL); ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL); // Вставляем столбцы memset(&lvc, 0, sizeof(lvc)); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | 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); // Вставляем строки memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM; lvi.pszText = LPSTR_TEXTCALLBACK; for(i=0; i<9; i++) { lvi.iItem = i; lvi.iSubItem = 0; lvi.cchTextMax = 40; lvi.lParam = (LPARAM)&rgApplInfo[i]; lvi.iImage = i; ListView_InsertItem(hwndList, &lvi); lvi.iItem = i; lvi.iSubItem = 1; ListView_InsertItem(hwndList, &lvi); lvi.iItem = i; lvi.iSubItem = 2; ListView_InsertItem(hwndList, &lvi); } return TRUE; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { DestroyWindow(hwndList); PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { DWORD dwStyle = 0; switch (id) { case ID_OPTIONS_ICONVIEW: { dwStyle = GetWindowLong(hwndList, GWL_STYLE); if((dwStyle & LVS_TYPEMASK) != LVS_ICON) SetWindowLong(hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_ICON); break; } case ID_OPTIONS_SMALLICONVIEW: { dwStyle = GetWindowLong(hwndList, GWL_STYLE); if((dwStyle & LVS_TYPEMASK) != LVS_SMALLICON) SetWindowLong(hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_SMALLICON); break; } case ID_OPTIONS_LISTVIEW: { dwStyle = GetWindowLong(hwndList, GWL_STYLE); if((dwStyle & LVS_TYPEMASK) != LVS_LIST) SetWindowLong(hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_LIST); break; } case ID_OPTIONS_REPORTVIEW: { dwStyle = GetWindowLong(hwndList, GWL_STYLE); if((dwStyle & LVS_TYPEMASK) != LVS_REPORT) SetWindowLong(hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_REPORT); break; } case ID_FILE_EXIT: PostQuitMessage(0); return 0L; break; case ID_HELP_ABOUT: break; default: break; } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } // ----------------------------------------------------- // Функция WndProc_OnNotify // ----------------------------------------------------- LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr) { LV_DISPINFO * lpLvdi = (LV_DISPINFO *)pnmhdr; APPLINFO * lpAppinfo = (APPLINFO *)(lpLvdi->item.lParam); static char szBuf[20]; NM_LISTVIEW *lpNm = (NM_LISTVIEW *)pnmhdr; if(idFrom != IDC_LISTVIEW) return 0L; switch(pnmhdr->code) { case LVN_GETDISPINFO: { if(lpLvdi->item.mask & LVIF_TEXT) { switch(lpLvdi->item.iSubItem) { case 0: lpLvdi->item.pszText = lpAppinfo->szAppName; break; case 1: lpLvdi->item.pszText = lpAppinfo->szIconName; break; case 2: itoa(lpAppinfo->iCost, szBuf, 10); lpLvdi->item.pszText = szBuf; break; default: break; } break; } } case LVN_COLUMNCLICK: { ListView_SortItems(lpNm->hdr.hwndFrom, LVCompareProc, (LPARAM)(lpNm->iSubItem)); return 0L; break; } case LVN_BEGINLABELEDIT: { return 0L; break; } case LVN_ENDLABELEDIT: { if((lpLvdi->item.iItem != -1) && (lpLvdi->item.pszText != NULL)) lstrcpy(lpAppinfo->szAppName, lpLvdi->item.pszText); return 0L; break; } case NM_DBLCLK: { int index; LV_ITEM lvi; char szBuf[256]; strcpy(szBuf, "Selected item:\n"); // Определяем номер выделенного элемента index = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); if(index == -1) return 0; // Подготавливаем структуру типа LV_ITEM // для получения текстовой информации об элементах memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_TEXT; // Получаем название элемента lvi.iItem = index; lvi.iSubItem = 0; ListView_GetItem(hwndList, &lvi); strcat(szBuf, lvi.pszText); // Получаем текстовую строку, связанную // с первым и вторым дополнительным элементом lvi.iItem = index; lvi.iSubItem = 1; ListView_GetItem(hwndList, &lvi); strcat(szBuf, " : "); strcat(szBuf, lvi.pszText); lvi.iItem = index; lvi.iSubItem = 2; ListView_GetItem(hwndList, &lvi); strcat(szBuf, " : $"); strcat(szBuf, lvi.pszText); // Выводим на экран текстовые строки // для выбранного элемента MessageBox(hWnd, szBuf, szAppName, MB_OK); return 0L; break; } } return 0L; } // ----------------------------------------------------- // Функция WndProc_OnSize // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy) { MoveWindow(hwndList, 0, 0, cx, cy, TRUE); return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc); } // ----------------------------------------------------- // Функция LVCompareProc // ----------------------------------------------------- int CALLBACK LVCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { APPLINFO *pAppInfo1 = (APPLINFO *)lParam1; APPLINFO *pAppInfo2 = (APPLINFO *)lParam2; LPSTR lpStr1, lpStr2; int iResult; if(pAppInfo1 && pAppInfo2) { switch(lParamSort) { case 0: lpStr1 = pAppInfo1->szAppName; lpStr2 = pAppInfo2->szAppName; iResult = strcmpi(lpStr1, lpStr2); break; case 1: lpStr1 = pAppInfo1->szIconName; lpStr2 = pAppInfo2->szIconName; iResult = lstrcmpi(lpStr1, lpStr2); break; case 2: iResult = pAppInfo1->iCost - pAppInfo2->iCost; break; default: iResult = 0; break; } } return(iResult); } Файл list.h (листинг 3.2) содержит описание функций и определение константы IDC_LISTVIEW (идентификатора органа управления List View). Листинг 3.2. Файл list\list.h // ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnDestroy(HWND hWnd); void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR FAR * pnmhdr); void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy); void WndProc_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem); int CALLBACK LVCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); #define IDC_LISTVIEW 1234 Файл описания ресурсов приложения, созданный для нашего проекта автоматически системой разработки Microsoft Visual C++, приведен в листинге 3.3. В нем определено главное меню приложения, пиктограммы, из которых формируются списки изображений для органа управления List View и таблица строк описания меню (в нашем приложении не используется, однако вы можете найти для нее применение, добавив орган управления Statusbar). Листинг 3.3. Файл list\list.rc //Microsoft Visual C++ generated resource script. #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // 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 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END ////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // 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 #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED Файл resource.h (листинг 3.4) также создается автоматически. В нем находятся определения констант для ресурсов приложения. Листинг 3.4. Файл list\resource.h //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by list.rc // #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 // Next default values for new objects // #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 View. Так как раньше мы уже приводили отдельные фрагменты этих функций, ограничимся кратким описанием. Глобальные переменныеМассив структур rgApplInfo предназначен для хранения элементов списка. Соответствующая структура имеет тип APPLINFO и определена следующим образом: typedef struct tagAPPLINFO { char szAppName[40]; // название приложения char szIconName[20]; // имя файла пиктограммы UINT iCost; // стоимость приложения } APPLINFO; В переменной hInst хранится идентификатор приложения, полученный функцией WinMain. Строчные массивы szAppName и szAppTitle хранят, соответственно, имя и заголовок приложения. Переменная hwndList нужна для хранения идентификатора созданного органа управления List View. WinMainФункция WinMain сохраняет идентификатор приложения и проверяет, не было ли это приложение запущено ранее. Если было, то активизируется окно работающего приложения. Далее функция регистрирует класс главного окна приложения, создает и отображает это окно, а затем запускает обычный цикл обработки сообщений. WndProcВ задачу функции WndProc входит обработка следующих сообщений: WM_CREATE, WM_DESTROY, WM_COMMAND, WM_NOTIFY, WM_SIZE. Обработка выполняется с использованием макрокоманды HANDLE_MSG. 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, изменяет размеры окна органа управления. Изменения выполняются функцией WndProc_OnSize. LVCompareProcФункция LVCompareProc нужна для сортировки элементов списка. Она выполняет сравнение двух элементов списка. Через первые два параметра передаются адреса структур данных, соответствующих сравниваемым элементам. Последний параметр содержит номер дополнительного элемента или нуль, если сравниваются названия элементов. Текстовые строки сравниваются при помощи функции strcmpi. Для сравнения численных значений мы использовали обычную операцию вычитания. |