Самоучитель программирования на VBA в Microsoft Office 966-7323-17-X

Книга адресована широкому кругу пользователей Microsoft Office, версий 98, 2000 и XP, желающих автоматизировать свою раб

196 37 21MB

Russian Pages 314 Year 2001

Report DMCA / Copyright

DOWNLOAD PDF FILE

Recommend Papers

Самоучитель программирования на VBA в Microsoft Office
 966-7323-17-X

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

Õîðåâ Â. Ä.

Ñàìîó÷èòåëü ïðîãðàììèðîâàíèÿ íà VBA â Microsoft Office

Êèåâ “ÞÍÈÎД 2001

ББК 32.973–01 Х79 УДК 681.3.06

Хорев В.Д. Х79 Самоучитель программирования на VBA в Microsoft Office/. — К.: Юниор, 2001. — 320 с., ил. ISBN 966–7323–17–Х Книга адресована широкому кругу пользователей Microsoft Office, версий 98, 2000 и XP, желающих автоматизировать свою работу при помощи Visual Basic for Application (VBA) и эффективных приемов работы в Microsoft Office. Данная книга является практическим руководством для пользователей любой квалификации, желающих самостоятельно научиться программировать на VBA. Каждая глава книги предлагает пользователю множество работающих рецептов для автоматизации современных задач в разных приложениях Microsoft Office, а приложение представляет собой краткий справочник по языку VBA, в котором теоретические сведения о языке умело переплетены с множеством примеров и практических советов. Седьмая глава и соответствующий раздел девятой главы являются обзорно-практическими по объектной модели Microsoft Office вцелом и отодельно по объектным моделям Word, Excel, Access и Outlook. Достоинством книги является всестороннее рассмотрение механизма взаимосвязи приложений Microsoft Office на основе использования объектных библиотек соответствующих приложений. Поседняя глава содержит бесценный материал по методам программирования в Microsoft Outlook. Это мало изученная область программирования умело раскрыта автором на примере создания готового приложения в MS Excel.

ББК 32.973–01

Все названия программных продуктов, устройств и технологий, описанных в данной книге, являются зарегистрированными торговыми марками соответствующих фирм. Все права защищены законодательством Украины и международным законодательством об авторском праве. Никакая часть этой книги, ни в каких целях не может быть воспроизведена в любой форме и любыми средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель или иные средства копирования или сохранения информации без письменного разрешения издательства.

ISBN 966–7323–17–Х

© ООО “Юниор”, 2001

Краткое оглавление ÏÐÅÄÈÑËÎÂÈÅ....................................................................................17 ÇÀ×ÅÌ ÝÒÀ ÊÍÈÃÀ ........................................................................................ 17 ÑÒÀÐÈÍÍÛÉ ÑÏÎÑÎÁ: “ÄÅËÀÉ, ÊÀÊ ß!” ............................................................. 17 Î ×ÅÌ ÝÒÀ ÊÍÈÃÀ ........................................................................................ 18 ×ÒÎ ÏÎÒÐÅÁÓÅÒÑß ÄËß ÐÀÁÎÒÛ Ñ ÝÒÎÉ ÊÍÈÃÎÉ ................................................... 18 Î ÌÀÊÐÎÑÀÕ È ÌÀÊÐÎÂÈÐÓÑÀÕ ........................................................................ 18 ÃËÀÂÀ 1. EXCEL: CÎÇÄÀÍÈÅ ÏÐÀÉÑ-ËÈÑÒÀ ............................................21 ×ÒÎ ÌÎÆÅÒ ÁÛÒÜ ÏÐÎÙÅ ÏÐßÌÎÓÃÎËÜÍÎÉ ÒÀÁËÈÖÛ? .......................................... 21 ÊÀÊ ÎÒÄÅËÈÒÜ ÂÍÓÒÐÅÍÍÞÞ ÈÍÔÎÐÌÀÖÈÞ ÎÒ ÂÍÅØÍÅÉ ÈÍÔÎÐÌÀÖÈÈ .................... 27 ÌÅÒÎÄÛ ÐÀÇÄÅËÅÍÈß ÈÍÔÎÐÌÀÖÈÈ ÍÀ “ÂÍÓÒÐÅÍÍÞÞ” È “ÂÍÅØÍÞÞ” .................... 30 VBA — ÝÒÎ Î×ÅÍÜ ÏÐÎÑÒÎ! ........................................................................... 31 ÏÅÐÂÎÅ ÇÍÀÊÎÌÑÒÂÎ Ñ VISUAL BASIC ............................................................... 32 ÃÅÍÅÐÀÖÈß ÊËÈÅÍÒÑÊÎÃÎ ÏÐÀÉÑ-ËÈÑÒÀ ........................................................... 38 ÃËÀÂÀ 2. EXCEL: ÏÐÀÉÑ-ËÈÑÒ ÄËß ÀÂÒÎÌÀÒÈ×ÅÑÊÎÃÎ ÑÎÑÒÀÂËÅÍÈß ÇÀÊÀÇÀ ........................................................................50 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................... 50 ÂÛÁÎÐ ÍÀÈÌÅÍÎÂÀÍÈß ÈÇ ÑÏÈÑÊÀ .................................................................. 51 ÈÌÅÍÎÂÀÍÍÛÅ ÄÈÀÏÀÇÎÍÛ ........................................................................... 51 ÏÎÑÒÐÎÅÍÈÅ ÁËÀÍÊÀ ÇÀÊÀÇÀ.......................................................................... 53 ÐÅØÅÍÈÅ ÏÐÎÁËÅÌÛ ÑÎÂÌÅÑÒÈÌÎÑÒÈ ÂÛÁÈÐÀÅÌÛÕ ÓÑÒÐÎÉÑÒ ÊÎÌÏÜÞÒÅÐÀ .......... 58 ÂÛÏÈØÈÒÅ Ñ×ÅÒ, ÏÎÆÀËÓÉÑÒÀ… .................................................................... 59 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................... 60 ÃËÀÂÀ 3. ÏÎÂÅÐÊÀ ÃÀÐÌÎÍÈÈ WORD ÀËÃÅÁÐÎÉ EXCEL...........................73 ÈÇ ÄÎÊÓÌÅÍÒÀ WORD  ÒÀÁËÈÖÓ EXCEL ............................................................ 74 ÎÁÐÀÒÍÎ ÈÇ ÒÀÁËÈÖÛ EXCEL  ÄÎÊÓÌÅÍÒ WORD................................................. 92 ÃËÀÂÀ 4. ÏÐÀÊÒÈÊÓÌ ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈß ÍÀ VBA ÄËß EXCEL È WORD 102 ÑÓÌÌÀ ÏÐÎÏÈÑÜÞ .................................................................................... 102 ×ÀÑÒÎÒÍÛÉ ÑËÎÂÀÐÜ ................................................................................. 112 ÑÐÀÂÍÈÒÅËÜÍÛÉ ÀÍÀËÈÇ ÏÐÀÉÑ-ËÈÑÒΠ........................................................ 119 ÃËÀÂÀ 5. ACCESS: ÑÎÇÄÀÍÈÅ ÎÔÈÑÍÎÉ ÁÀÇÛ ÄÀÍÍÛÕ ....................... 131 ÑÎÇÄÀÍÈÅ ÁÀÇÛ ÄÀÍÍÛÕ MS ACCESS ............................................................ 131 ÑÎÇÄÀÍÈÅ ÒÀÁËÈÖ .................................................................................... 131 ÂÂÎÄ ÄÀÍÍÛÕ Â ÁÀÇÓ ÄÀÍÍÛÕ ACCESS ........................................................... 139 ÑÎÇÄÀÍÈÅ ÝÊÐÀÍÍÛÕ ÔÎÐÌ ........................................................................ 139 ÓÑÎÂÅÐØÅÍÑÒÂÓÅÌ ÔÎÐÌÓ ÏÐÈ ÏÎÌÎÙÈ VBA................................................. 145 ÃËÀÂÀ 6. ACCESS: ÀÂÒÎÌÀÒÈÇÀÖÈß ÎÔÈÑÍÎÉ ÁÀÇÛ ÄÀÍÍÛÕ..............149 ÀÂÒÎÌÀÒÈÇÀÖÈß ÂÂÎÄÀ ÄÀÍÍÛÕ ................................................................... 149 ÀÂÒÎÌÀÒÈÇÀÖÈß ÐÀÁÎÒÛ Ñ ÈÌÅÞÙÈÌÈÑß ÄÀÍÍÛÌÈ......................................... 154 ÃËÀÂÀ 7.  ÌÈÐÅ ÎÁÚÅÊÒΠMS OFFICE .............................................. 167

4 Краткое оглавление ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS WORD .................................................................. 167 ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS EXCEL .................................................................. 174 ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS ACCESS ................................................................ 182 ÃËÀÂÀ 8. VBA-ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ: ÏÐÈÌÅÐÛ È ÈËËÞÑÒÐÀÖÈÈ ......... 190 ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ ÎÁÚÅÊÒΠÈÍÒÅÃÐÈÐÎÂÀÍÍÎÉ ÑÐÅÄÛ MS OFFICE .................. 191 ÔÎÐÌÀÒÈÐÎÂÀÍÈÅ ÎÁÚÅÊÒΠÏÐÈËÎÆÅÍÈÉ MS OFFICE ..................................... 202 ÏÐÅÄÑÒÀÂËÅÍÈÅ ÄÀÍÍÛÕ Â MS OFFICE ........................................................... 210 ÃËÀÂÀ 9. ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ Â OUTLOOK: ÄÎÊÓÌÅÍÒÎÎÁÎÐÎÒ È ÝËÅÊÒÐÎÍÍÀß ÏÎ×ÒÀ ........................................................................ 223 ÝËÅÊÒÐÎÍÍÀß ÏÎ×ÒÀ  ÑÎÂÐÅÌÅÍÍÎÌ ÎÔÈÑÅ .................................................. 223 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................. 223 ÎÁÚÅÊÒÍÀß ÌÎÄÅËÜ OUTLOOK 2000.............................................................. 224 ÑÎÇÄÀÍÈÅ Ó×ÅÒÍÛÕ ÇÀÏÈÑÅÉ ....................................................................... 231 ÑÎÇÄÀÍÈÅ ÏÐÈËÎÆÅÍÈß REGISTRATOR ........................................................... 234 ÏÐÈËÎÆÅÍÈÅ. VBA, ÊÀÊ ßÇÛÊ ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈß: ÄÀÍÍÛÅ, ÑÈÍÒÀÊÑÈÑ È ÔÓÍÊÖÈÈ.................................................................... 262 ÄÀÍÍÛÅ VBA: ÒÈÏÛ ÄÀÍÍÛÕ, ÏÅÐÅÌÅÍÍÛÅ È ÊÎÍÑÒÀÍÒÛ ................................... 262 ÑÈÍÒÀÊÑÈ×ÅÑÊÈÅ ÊÎÍÑÒÐÓÊÖÈÈ ßÇÛÊÀ VBA ................................................... 274 ÎÏÅÐÀÒÎÐÛ È ÂÑÒÐÎÅÍÍÛÅ ÔÓÍÊÖÈÈ ßÇÛÊÀ VBA ............................................. 282

Оглавление ÏÐÅÄÈÑËÎÂÈÅ....................................................................................17 ÇÀ×ÅÌ ÝÒÀ ÊÍÈÃÀ ........................................................................................ 17 ÑÒÀÐÈÍÍÛÉ ÑÏÎÑÎÁ: “ÄÅËÀÉ, ÊÀÊ ß!” ............................................................. 17 Î ×ÅÌ ÝÒÀ ÊÍÈÃÀ ........................................................................................ 18 ×ÒÎ ÏÎÒÐÅÁÓÅÒÑß ÄËß ÐÀÁÎÒÛ Ñ ÝÒÎÉ ÊÍÈÃÎÉ ................................................... 18 Î ÌÀÊÐÎÑÀÕ È ÌÀÊÐÎÂÈÐÓÑÀÕ ........................................................................ 18 Îïàñíû ëè ìàêðîñû?............................................................................ 18 Ãäå îíè íàõîäÿòñÿ? .............................................................................. 18 Íàäî ëè áîÿòüñÿ ìàêðîñîâ?.................................................................. 19 Óðîâíè áåçîïàñíîñòè â Office 2000....................................................... 19 ÃËÀÂÀ 1 EXCEL: CÎÇÄÀÍÈÅ ÏÐÀÉÑ-ËÈÑÒÀ..........................................................21 ×ÒÎ ÌÎÆÅÒ ÁÛÒÜ ÏÐÎÙÅ ÏÐßÌÎÓÃÎËÜÍÎÉ ÒÀÁËÈÖÛ? .......................................... 21 Òîâàðû è öåíû “â ñòîëáèê” ................................................................... 21 Êàê îòôîðìàòèðîâàòü ñòîëáåö äëÿ ââîäà äåíåæíûõ çíà÷åíèé................ 22 Ñîçäàíèå çàãîëîâêà ðàçäåëà ................................................................ 23 Ôîðìàòèðîâàíèå äèàïàçîíà ÿ÷ååê ïî îáðàçöó ...................................... 23 Êàê ôîðìàòèðîâàòü ðàçäåë ïðè ïîìîùè êîìàíäû Ñïåöèàëüíàÿ âñòàâêà ............ 24

Óäàëåíèå è âñòàâêà ñòðîê..................................................................... 25 Ìåõàíèçìîì ãðóïïèðîâàíèÿ äàííûõ ..................................................... 25 Êàê ãðóïïèðîâàòü äàííûå..................................................................................... 25

Çàãîëîâîê ïðàéñ-ëèñòà......................................................................... 26 ÊÀÊ ÎÒÄÅËÈÒÜ ÂÍÓÒÐÅÍÍÞÞ ÈÍÔÎÐÌÀÖÈÞ ÎÒ ÂÍÅØÍÅÉ ÈÍÔÎÐÌÀÖÈÈ .................... 27 Ïîñòàíîâêà çàäà÷è ............................................................................... 27 Èìåíîâàííûå ÿ÷åéêè............................................................................ 28 Èñïîëüçîâàíèå ôîðìóë è àâòîçàïîëíåíèå ÿ÷ååê ôîðìóëàìè................. 29 Óñëîâíîå ôîðìàòèðîâàíèå ÿ÷ååê.......................................................... 29 Êàê ïðèìåíèòü ê ÿ÷åéêàì óñëîâíîå ôîðìàòèðîâàíèå.......................................... 29

ÌÅÒÎÄÛ ÐÀÇÄÅËÅÍÈß ÈÍÔÎÐÌÀÖÈÈ ÍÀ “ÂÍÓÒÐÅÍÍÞÞ” È “ÂÍÅØÍÞÞ” .................... 30 Ñâÿçûâàíèå äàííûõ â Excel................................................................... 31 Íåäîñòàòêè ñâÿçûâàíèÿ ëèñòîâ Excel .................................................... 31 VBA — ÝÒÎ Î×ÅÍÜ ÏÐÎÑÒÎ! ........................................................................... 31 ÏÅÐÂÎÅ ÇÍÀÊÎÌÑÒÂÎ Ñ VISUAL BASIC ............................................................... 32 Çàïóñê ðåäàêòîðà Visual Basic............................................................... 32 Ñîçäàíèå ïðîöåäóðû VBA..................................................................... 32 Ââîä èñõîäíîãî òåêñòà ïðîöåäóðû........................................................ 33 Âîçìîæíûå ïðîáëåìû è ìåòîäû èõ ðåøåíèÿ ........................................ 34 Àíàëèç ñòàíäàðòíîé ïðîöåäóðû îáðàáîòêè ñîáûòèÿ — äâîéíîãî ùåë÷êà ìûøüþ íà ÿ÷åéêå ëèñòà Excel ............................................................... 35 Åùå îäíî ñîáûòèå — åùå îäèí ìàëåíüêèé ìàêðîñ ................................ 36 Àâòîìàòè÷åñêîå âûäåëåíèå ïîçèöèé â ïðàéñå .................................................... 37

Àíàëèç ñòàíäàðòíîé ïðîöåäóðû îáðàáîòêè ñîáûòèÿ — àêòèâèçàöèÿ ðàáî÷åãî ëèñòà Excel............................................................................ 38 ÃÅÍÅÐÀÖÈß ÊËÈÅÍÒÑÊÎÃÎ ÏÐÀÉÑ-ËÈÑÒÀ ........................................................... 38

6 Оглавление Êàê ïîìåñòèòü íà ëèñò ýëåìåíò óïðàâëåíèÿ .......................................... 39 Îñîáåííîñòè èçìåíåíèÿ ñâîéñòâ ýëåìåíòîâ óïðàâëÿþùèõ ðàáî÷èì ëèñòîì................................................................................................. 40 Ïðîñòåéøèé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà .............................. 40 Àíàëèç ïðîöåäóðû ãåíåðàöèè ïðàéñà .................................................................. 41 Íåäîñòàòêè ïðîñòåéøå ïðîöåäóðû ãåíåðàöèè ïðàéñà......................................... 42

Óñîâåðøåíñòâîâàíèÿ ïðîöåäóðû ãåíåðàöèè ïðàéñà .............................. 42 Îãðàíè÷åíèå îáëàñòè êîïèðîâàíèÿ ÿ÷ååê ............................................................ 42 Èçìåíåíèå øèðèíû ÿ÷ååê è ñòîëáöîâ ................................................................. 43 Èçìåíåíèå èìåíè ðàáî÷åãî ëèñòà Excel .............................................................. 43 Òåñòèðîâàíèå ðàáîòû íîâîé ïðîöåäóðû ãåíåðàöèè ïðàéñà ................................ 43 Êîïèðîâàíèå ðåçóëüòàòîâ âû÷èñëåíèé ïî ôîðìóëàì è èõ ôîðìàòèðîâàíèÿ .......44 Èñêëþ÷åíèå ïîâòîðåíèÿ èìåí ëèñòîâ ðàáî÷åé êíèãè Excel .................................44

Ïîëíîöåííûé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà............................. 45 Íåäîñòàòêè íîâîãî âàðèàíòà ïðîöåäóðû ãåíåðàöèè ïðàéñà ................................ 47

Îêîí÷àòåëüíûé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà .......................... 47 ÃËÀÂÀ 2 EXCEL: ÏÐÀÉÑ-ËÈÑÒ ÄËß ÀÂÒÎÌÀÒÈ×ÅÑÊÎÃÎ ÑÎÑÒÀÂËÅÍÈß ÇÀÊÀÇÀ ........................................................................50 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................... 50 ÂÛÁÎÐ ÍÀÈÌÅÍÎÂÀÍÈß ÈÇ ÑÏÈÑÊÀ .................................................................. 51 ÈÌÅÍÎÂÀÍÍÛÅ ÄÈÀÏÀÇÎÍÛ ........................................................................... 51 Êàê ñîçäàòü èìåíîâàííûé äèàïàçîí...................................................... 52 ÏÎÑÒÐÎÅÍÈÅ ÁËÀÍÊÀ ÇÀÊÀÇÀ.......................................................................... 53 Êàê ñîçäàòü è íàñòðîèòü ýëåìåíò óïðàâëåíèÿ Ïîëå ñî ñïèñêîì ............. 54 Îïèñàíèå ðàáîòû ýëåìåíòà óïðàâëåíèÿ Ïîëå ñî ñïèñêîì ..................... 55 Èñïîëüçîâàíèå ôóíêöèè ÈÍÄÅÊÑ()....................................................... 56 Ñîçäàíèå â áëàíêå çàêàçà “áåçâàðèàíòíûõ” ñòðîê ................................. 57 Ïîäñ÷åò ñóììû çàêàçà.......................................................................... 58 ÐÅØÅÍÈÅ ÏÐÎÁËÅÌÛ ÑÎÂÌÅÑÒÈÌÎÑÒÈ ÂÛÁÈÐÀÅÌÛÕ ÓÑÒÐÎÉÑÒ ÊÎÌÏÜÞÒÅÐÀ .......... 58 Ïîñòàíîâêà çàäà÷è ............................................................................... 58 Ñîçäàíèå ïðîöåäóðû Ðàñêðñïèñîê2_Èçìåíåíèå().................................. 58 ÂÛÏÈØÈÒÅ Ñ×ÅÒ, ÏÎÆÀËÓÉÑÒÀ… .................................................................... 59 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................... 60 Ýëåìåíò óïðàâëåíèÿ Êíîïêà................................................................. 60 Ïåðâûé ñïîñîá ôîðìèðîâàíèÿ ñ÷åòà (èñïîëüçîâàíèå ìàêðîðåêîðäåðà). 60 Èñïîëüçîâàíèå ôóíêöèè ÑÅÃÎÄÍß().................................................................... 61 Êàê çàïèñàòü ìàêðîñ ïðè ïîìîùè ìàêðîðåêîðäåðà ............................................ 61 Àíàëèç êîäà àâòîìàòè÷åñêè ñîçäàííîãî ìàêðîñà ................................................ 62

Âòîðîé ñïîñîá ôîðìèðîâàíèÿ ñ÷åòà (ìàêðîñ ëèñòà Excel ðàáîòàåò ñ äîêóìåíòîì Word)................................................................................ 63 Ïîñòàíîâêà çàäà÷è ............................................................................................... 63 Ñîçäàíèå îáðàçöà ñ÷åòà â Word .......................................................................... 64 Êàê âñòàâèòü ïîëÿ â äîêóìåíò Word.................................................................. 64 Ñîçäàíèå ìàêðîñà ðàáî÷åãî ëèñòà Excel äëÿ ðàáîòû ñ äîêóìåíòîì Word ........... 65 Êàê çàäàòü ññûëêó íà áèáëèîòåêó îáúåêòîâ Word â ñðåäå MS Excel................. 66 Àíàëèç ïðîöåäóðû äëÿ ðàáîòû ñ äîêóìåíòîì Word.......................................... 67 Ñîçäàíèå òàáëèöû â äîêóìåíòå Word è åå àâòîôîðìàòèðîâàíèå................................. 68

Óñîâåðøåíñòâîâàíèÿ ìàêðîñà ðàáî÷åãî ëèñòà Excel äëÿ ðàáîòû ñ äîêóìåíòîì Word..................................................................................................................... 69 Ñîçäàíèå äèàëîãîâîãî îêíà ïðè ïîìîùè ôóíêöèè MsgBox() ........................... 69

ÃËÀÂÀ 3 ÏÎÂÅÐÊÀ ÃÀÐÌÎÍÈÈ WORD ÀËÃÅÁÐÎÉ EXCEL ........................................73

Оглавление 7 ÊÎÅ-×ÒÎ Î ÑÎÂÐÅÌÅÍÍÎÌ ÄÎÊÓÌÅÍÒÎÎÁÎÐÎÒÅ .................................................. 73 ÈÇ ÄÎÊÓÌÅÍÒÀ WORD  ÒÀÁËÈÖÓ EXCEL ............................................................ 74 Ïîñòàíîâêà çàäà÷è ............................................................................... 74 Ïîäãîòîâêà òàáëèöû äîêóìåíòîâ........................................................... 74 Ïðèâÿçêà ìàêðîñà ê êîìàíäå Word........................................................ 75 Êàê çàìåíèòü êîìàíäó Word................................................................................. 75

Ñîçäàåì ìàêðîñ-àðõèâàðèóñ Word-äîêóìåíòîâ ..................................... 77 Êàê çàäàòü ññûëêó íà áèáëèîòåêó îáúåêòîâ Excel â ñðåäå MS Word .................... 78 Îïèñàíèå ðàáîòû ìàêðîñà-àðõèâàðèóñà Word-äîêóìåíòîâ ................................. 78 Àíàëèç êîäà äåéñòâóþùåé ìîäåëè àðõèâàðèóñà Word-äîêóìåíòîâ â Excel ......... 79 Àâòîìàòè÷åñêîå îòîáðàæåíèå íà ýêðàíå áèáëèîòåêè îáúåêòîâ Excel .............. 80 Îñâîáîæäåíèå ñèñòåìíîé ïàìÿòè êîìïüþòåðà ................................................ 80 Óñîâåðøåíñòâîâàíèÿ äåéñòâóþùåé ìîäåëè äëÿ ñîçäàíèÿ ìàêðîñààðõèâàðèóñà Word-äîêóìåíòîâ............................................................................. 81 Àíàëèç ïåðâîãî ñëîâà Word-äîêóìåíòà ............................................................ 81

Ìàêðîñ-àðõèâàðèóñ àíàëèçèðóåò ñîäåðæèìîå Word-äîêóìåíòà ............. 84 Ïîñòàíîâêà çàäà÷è ............................................................................................... 84 Ðåàëèçàöèÿ ìåõàíèçìà àíàëèçà ñîäåðæèìîãî Word-äîêóìåíòà .......................... 85 Àíàëèç çíà÷åíèé ñëîâ â äîêóìåíòå Word............................................................. 86 Ïîñòàíîâêà çàäà÷è ........................................................................................... 86 Àíàëèç ñëîâà Word-äîêóìåíòà.......................................................................... 87 Ïîñèìâîëüíûé àíàëèç ñëîâ â äîêóìåíòå Word .................................................... 88 Ïîñòàíîâêà çàäà÷è ........................................................................................... 88 Ïðèìåíåíèå ëîãè÷åñêîé îïåðàöèè Or (ëîãè÷åñêîå ÈËÈ) ................................. 88 Ïðèìåíåíèå ëîãè÷åñêîé îïåðàöèè And (ëîãè÷åñêîå È).................................... 89 Óïðàâëåíèå îøèáêàìè âûïîëíåíèÿ ìàêðîñà ....................................................... 89 Óñîâåðøåíñòâîâàíèÿ àíàëèçàòîðà ñîäåðæèìîãî Word-äîêóìåíòà ...................... 92 Îïòèìèçàöèÿ êîäà àíàëèçàòîðà Word-äîêóìåíòà ............................................. 92

ÎÁÐÀÒÍÎ ÈÇ ÒÀÁËÈÖÛ EXCEL  ÄÎÊÓÌÅÍÒ WORD................................................. 92 Åùå îäíî “ñòðàøíîå” ñëîâî — OLE....................................................... 93 Ïîñòàíîâêà çàäà÷è ............................................................................................... 93 Ïîäãîòîâêà òàáëèöû ............................................................................................ 93 Ñâÿçûâàíèå òàáëèöû ñ äîêóìåíòîì ..................................................................... 95 Êàê ñâÿçàòü ÿ÷åéêó ðàáî÷åãî ëèñòà Excel ñ òåêñòîâîé ïîçèöèåé â äîêóìåíòå Word ................................................................................................................. 95

È ñíîâà VBA......................................................................................... 96 Ïîñòàíîâêà çàäà÷è ............................................................................................... 96 Ìàêðîñ, êîïèðóþùèé òåêóùóþ ñòðîêó â ñòðîêó 2 ................................................ 96 Åùå ïàðà ìàêðîñîâ, íåîáõîäèìûõ äëÿ àâòîìàòè÷åñêîãî îòêðûòèÿ è çàêðûòèÿ îêíà Word............................................................................................................. 97 Ïîñòàíîâêà çàäà÷è ........................................................................................... 97 Îáðàáîò÷èê ñîáûòèÿ ðàáî÷åãî ëèñòà Activate................................................... 98 Îáðàáîò÷èê ñîáûòèÿ ðàáî÷åãî ëèñòà Deactivate............................................... 98

ÃËÀÂÀ 4 ÏÐÀÊÒÈÊÓÌ ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈß ÍÀ VBA ÄËß EXCEL È WORD ............. 102 ÑÓÌÌÀ ÏÐÎÏÈÑÜÞ .................................................................................... 102 Ìîäóëè Visual Basic ............................................................................ 102 Êàê äîáàâèòü ê ðàáî÷åé êíèãå Excel ïðîãðàììíûé ìîäóëü ................................ 102

Ôóíêöèè ............................................................................................ 103 Ïîñòàíîâêà çàäà÷è ............................................................................. 103 Êàê äîáàâèòü â ìîäóëü ïðîöåäóðó-ôóíêöèþ ...................................................... 103

Ðàçðàáàòûâàåì èñõîäíûé òåêñò ôóíêöèè Ñóììà_ïðîïèñüþ() ............... 104 Èñïîëüçîâàíèå ôóíêöèè FORMAT () äëÿ ïðåîáðàçîâàíèÿ ÷èñëà â ñòðîêó ......... 104 Àíàëèç òåêñòîâîé ñòðîêè ................................................................................... 105

Èñïîëüçîâàíèå ôóíêöèè Ñóììà_ïðîïèñüþ()........................................ 110 Ñîçäàíèå ïðîöåäóðû-îáðàáîò÷èêà ñîáûòèÿ Change äëÿ ëèñòà ðàáî÷åé êíèãè Excel ......................................................................................................... 110

8 Оглавление Çàùèòà îò âîçíèêíîâåíèÿ êàñêàäíûõ ñîáûòèé .................................................. 111

×ÀÑÒÎÒÍÛÉ ÑËÎÂÀÐÜ ................................................................................. 112 Ïîñòàíîâêà çàäà÷è ............................................................................. 112 Ðàçðàáàòûâàåì èñõîäíûé òåêñò ìàêðîñà............................................. 112 Èñïîëüçîâàíèå ìàññèâîâ................................................................................... 112 Îáúÿâëåíèå êîíñòàíò ..................................................................................... 112 Ñîçäàíèå Word-äîêóìåíòîâ è íîâûõ àáçàöåâ â íèõ ïðè ïîìîùè ìåòîäà Add ... 115 Óïîðÿäî÷èâàíèå ýëåìåíòîâ ìàññèâà ïî óáûâàíèþ............................................ 115 Ïðåîáðàçîâàíèå ñòðîê Word-äîêóìåíòà â òàáëèöó ïðè ïîìîùè ìåòîäà ConvertToTable ................................................................................................... 116 Àâòîôîðìàò òàáëèöû ......................................................................................... 116

Èñïîëüçîâàíèå ïðîöåäóðû ×àñòîòíûé_ñëîâàðü ................................... 119 ÑÐÀÂÍÈÒÅËÜÍÛÉ ÀÍÀËÈÇ ÏÐÀÉÑ-ËÈÑÒΠ........................................................ 119 Âõîäÿùèå ïðàéñ-ëèñòû ...................................................................... 120 Ïîñòàíîâêà çàäà÷è ............................................................................. 120 Ñâîäíûé ïðàéñ-ëèñò .......................................................................... 121 Ïîäêëþ÷åíèå àíàëèçèðóåìûõ ïðàéñ-ëèñòîâ ...................................................... 121 Ïîäãîòîâêà ðàáî÷åãî ëèñòà äëÿ ñâîäíîãî ïðàéñà.............................................. 122

Ñîçäàíèå ïðîöåäóðû Ñâîäêà()............................................................ 123 Àâòîìàòè÷åñêîå çàêðûòèå ôàéëà ñ îòìåíîé ñäåëàííûõ â íåì èçìåíåíèé ........ 125

Îñîáåííîñòè èñõîäíûõ äàííûõ èç ñðàâíèâàåìûõ ïðàéñ ëèñòîâ ........... 128 ÃËÀÂÀ 5 ACCESS: ÑÎÇÄÀÍÈÅ ÎÔÈÑÍÎÉ ÁÀÇÛ ÄÀÍÍÛÕ..................................... 131 ÑÎÇÄÀÍÈÅ ÁÀÇÛ ÄÀÍÍÛÕ MS ACCESS ............................................................ 131 ÑÎÇÄÀÍÈÅ ÒÀÁËÈÖ .................................................................................... 131 Âñïîìîãàòåëüíûå òàáëèöû−ñïðàâî÷íèêè............................................. 132 Ñîçäàíèå ñïðàâî÷íèêà êëèåíòîâ ôèðìû............................................................ 132 Êàê ñîçäàòü òàáëèöó â ðåæèìå êîíñòðóêòîðà ................................................. 132 Ñîçäàíèå ñïðàâî÷íèêà îáðàùåíèé ê êëèåíòàì ôèðìû...................................... 134

Òàáëèöà “Êëèåíòû”............................................................................. 134 Ïîäñòàíîâî÷íûå ïîëÿ ........................................................................................ 135 Êàê ñîçäàòü ïîäñòàíîâî÷íûå ïîëÿ ïðè ïîìîùè Ìàñòåðà ïîäñòàíîâîê .......... 135

Òàáëèöà “Îïåðàöèè”........................................................................... 137 Ñîçäàíèå ñïðàâî÷íèêà òèïîâ îïåðàöèé............................................................. 137 Ñîçäàíèå òàáëèöû îïåðàöèé ............................................................................. 138

ÂÂÎÄ ÄÀÍÍÛÕ Â ÁÀÇÓ ÄÀÍÍÛÕ ACCESS ........................................................... 139 ÑÎÇÄÀÍÈÅ ÝÊÐÀÍÍÛÕ ÔÎÐÌ ........................................................................ 139 Ôîðìà “Âñå îïåðàöèè”....................................................................... 139 Êàê ñîçäàòü ýêðàííóþ ôîðìó ïðè ïîìîùè Ìàñòåðà ôîðì ................................ 139 Ââîä äàííûõ ïðè ïîìîùè ôîðìû “Âñå îïåðàöèè” ............................................ 140

Ïîä÷èíåííàÿ ôîðìà “Îïåðàöèè ïî êëèåíòó” ....................................... 140 Ôîðìà “Êëèåíòû” ............................................................................... 141 Ñîçäàíèå ïðîñòîé ôîðìû.................................................................................. 141 Äîáàâëåíèå ïîä÷èíåííîé ôîðìû ...................................................................... 141 Êàê ñîçäàòü ïîä÷èíåííóþ ôîðìó ïðè ïîìîùè Ìàñòåðà ïîä÷èíåííûõ ôîðì ..... 143 Êàê çàïðåòèòü èçìåíåíèå äàííûõ, îòîáðàæàåìûõ ôîðìîé................................ 144

ÓÑÎÂÅÐØÅÍÑÒÂÓÅÌ ÔÎÐÌÓ ÏÐÈ ÏÎÌÎÙÈ VBA................................................. 145 Àâòîìàòè÷åñêîå çàïîëíåíèå ïîëÿ Ñêèäêà............................................ 145 Ïîñòàíîâêà çàäà÷è ............................................................................................. 145 Êàê ñîçäàòü ïðîöåäóðó îáðàáîòêè ñîáûòèÿ äëÿ ýëåìåíòà ýêðàííîé ôîðìû ..... 146

À ó âàñ ñåãîäíÿ äåíü ðîæäåíèÿ!.......................................................... 147 Ïîñòàíîâêà çàäà÷è ............................................................................................. 147 Ñîçäàíèå îáðàáîò÷èêà ñîáûòèÿ Current ............................................................ 147 Èñïîëüçîâàíèå ôóíêöèé Month() è Day() ........................................................ 147

ÃËÀÂÀ 6

Оглавление 9 ACCESS: ÀÂÒÎÌÀÒÈÇÀÖÈß ÎÔÈÑÍÎÉ ÁÀÇÛ ÄÀÍÍÛÕ ...........................149 ÀÂÒÎÌÀÒÈÇÀÖÈß ÂÂÎÄÀ ÄÀÍÍÛÕ ................................................................... 149 Ïîñòàíîâêà çàäà÷è ............................................................................. 149 Ïîäãîòîâêà ðàáî÷åãî ëèñòà ................................................................ 150 Ññûëêà íà áèáëèîòåêó îáúåêòîâ MS Access ........................................ 151 Èñõîäíûé òåêñò ìàêðîñà, çàïèñûâàþùåãî îïåðàöèþ â áàçó äàííûõ Access ............................................................................................... 151 Àíàëèç ïðîöåäóðû Çàïèñü_îïåðàöèè .................................................. 152 Îáúåêòû MS Access ........................................................................................... 152 Èñïîëüçîâàíèå îáúåêòà DoCmd äëÿ îòêðûòèÿ ôîðìû.................................... 153 Ñîçäàíèå íîâîé çàïèñè è ïåðåõîä íà íåå...................................................... 153

ÀÂÒÎÌÀÒÈÇÀÖÈß ÐÀÁÎÒÛ Ñ ÈÌÅÞÙÈÌÈÑß ÄÀÍÍÛÌÈ......................................... 154 Ñîçäàíèå ïðîãðàììíîãî ìîäóëÿ â ñîñòàâå áàçû äàííûõ...................... 154 Ññûëêè íà áèáëèîòåêè îáúåêòîâ: DAO èëè ADO?................................. 156 Ôóíêöèÿ “Àíàëèç áàëàíñà ïî êëèåíòó” ................................................ 156 Ïðîñìîòð çàïèñåé òàáëèöû Access ïðè ïîìîùè öèêëà Do While…Loop ............ 157 Ñîçäàíèå SQL-çàïðîñà ...................................................................................... 158 Èñïîëüçîâàíèå ôóíêöèè MsgBox() äëÿ îðãàíèçàöèè èíòåðôåñà, ïðåäîñòàâëÿþùåãî ïîëüçîâàòåëþ âûáîðî÷íî óïðàâëÿòü âûïîëíåíèåì ïðîãðàììû ......................................................................................................... 159

Ïðèíÿòèå ðåøåíèÿ ïî ðåçóëüòàòàì àíàëèçà äàííûõ ............................ 160 Èñïîëüçîâàíèå îáúåêòîâ Word äëÿ ôîðìèðîâàíèÿ ïèñüìà èç äàííûõ òàáëèöû Access ................................................................................................................ 160 Îáúåêòû òèïà LetterContent ............................................................................ 160 Ïîñòàíîâêà çàäà÷è ......................................................................................... 161 Àâòîìàòè÷åñêàÿ ãåíåðàöèÿ ïèñüìà êëèåíòó.................................................... 161

Ïðîãðàììíûé êîä ôóíêöèè Àíàëèç_áàëàíñà........................................ 163 ÃËÀÂÀ 7 Â ÌÈÐÅ ÎÁÚÅÊÒÎÂ MS OFFICE............................................................ 167 ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS WORD .................................................................. 167 Ðàáîòà ñ îáúåêòàìè Word íà óðîâíå ïðèëîæåíèÿ è äîêóìåíòà............. 167 Îáúåêò Application .............................................................................................. 167 Ñâîéñòâî Caption .................................................................................................. ............................................................................................................................. ............................................................................................................................. ............................................................................................................................. ............................................................................................................................. ............................................................................................................................. ..................................................................................................................... 167 Ñâîéñòâà ActiveWindow, ActiveDocument è ActivePrinter .................................. 167 Ñåìåéñòâî CommandBars ............................................................................... 168 Ñâîéñòâà DisplayScrollBars è DisplayStatusBar ................................................ 168 Îáúåêò Options ............................................................................................... 168 Ñâîéñòâî AllowDragAndDrop: óïðàâëåíèå ðåæèìîì ïåðåòàñêèâàíèÿ òåêñòà â ðåæèìå Ïðàâêà ..................................................................................................................... 168 Ñâîéñòâî SaveInterval: çàäàíèå èíòåðâàëà àâòîñîõðàíåíèÿ ....................................... 168

Ñâîéñòâî StatusBar ......................................................................................... 168 Ìåòîä Quit ...................................................................................................... 168 Ñåìåéñòâî Documents .................................................................................... 168 Ìåòîä Add ............................................................................................................... 169 Ìåòîä Open ............................................................................................................. 169 Ìåòîäû Close è Activate............................................................................................ 169

Îáúåêòû ActiveDocument è ThisDocument ....................................................... 169 Ìåòîä PrintOut: ïå÷àòü äîêóìåíòà ............................................................................. Ìåòîä SendMail: îòïðàâêà äîêóìåíòà ïî ýëåêòðîííîé ïî÷òå, êàê âëîæåíèå ............... Ìåòîä Close ............................................................................................................. Ìåòîäû Save è SaveAs..............................................................................................

169 169 169 170

Îáúåêò Range: ðàáîòà ñ òåêñòîâûì ñîäåðæèìûì äîêóìåíòà............................. 170

10 Оглавление Ñâîéñòâî Bold: âûäåëåíèå äèàïàçîíà ïîëóæèðíûì íà÷åðòàíèåì åãî ñèìâîëîâ ........................................................................................................ 170 Ñâîéñòâî InsertBefore: âñòàâêà ñòðîêè............................................................ 170 Ïðîâåðêà ïðàâîïèñàíèÿ ................................................................................. 170 Çàäàíèå âåðõíåãî êîëîíòèòóëà....................................................................... 171 Ñåìåéñòâà Paragraphs, Sentences, Words è Characters ...................................... 171 Ñâîéñòâà First è Last ....................................................................................... 171 Ñâîéñòâà Count............................................................................................... 171 Ñâîéñòâî Alignment......................................................................................... 171 Ñîçäàíèå âîêðóã àáçàöà ðàìêó çàäàííîãî ñòèëÿ ............................................ 171 Âñòàâêà òåêñòà ïîñëå çàäàííîãî àáçàöà......................................................... 171 Èçìåíåíèå ôîíà àáçàöà................................................................................. 171 Èçìåíåíèå ñòèëÿ àáçàöà ................................................................................ 171 Èçìåíåíèå ñòèëÿ àáçàöà ................................................................................ 171 Ïåðåìåùåíèå ïî òåêñòó ................................................................................. 172 Ìåòîä Delete .................................................................................................. 172 Ïðåîáðàçîâàíèå ñèìâîëîâ â âåðõíèé ðåãèñòð ............................................... 172 Ñåìåéñòâî StoryRanges .................................................................................. 172

Ðàáîòà ñî ñïèñêàìè è òàáëèöàìè äîêóìåíòà Word .............................. 172 Word-òàáëèöû .................................................................................................... 172 Óïðàâëåíèå ãðàíèöàìè òàáëèö ....................................................................... 172 Óäàëåíèå ñòîëáöà òàáëèöû ............................................................................ 172 Èçìåíåíèå ôîíà òàáëèöû .............................................................................. 173 Ñîçäàíèå òàáëèöû .......................................................................................... 173 Àâòîôîðìàò òàáëèöû ...................................................................................... 173 Çàïèñü â ÿ÷åéêè òàáëèöû äàííûõ ................................................................... 173 Èçìåíåíèå øèðèíû ñòîëáöîâ òàáëèöû .......................................................... 173 Âûðàâíèâàíèå òåêñòà â òàáëèöå ..................................................................... 173 Äîáàâëåíèå çàãîëîâêà òàáëèöû ..................................................................... 173 Àâòîñóììèðîâàíèå ÿ÷ååê òàáëèöû ................................................................. 173 Ðàçáèåíèå ÿ÷ååê òàáëèöû .............................................................................. 174 Word-ñïèñêè....................................................................................................... 174 Îáúåêò ListFormat ........................................................................................... 174

ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS EXCEL .................................................................. 174 Ðàáîòà ñ îáúåêòàìè Excel íà óðîâíå ïðèëîæåíèÿ, ðàáî÷åé êíèãè è ëèñòà ................................................................................................. 175

Îáúåêò Application .............................................................................................. 175 Ñâîéñòâà Workbooks, ActiveWorkbook è ThisWorkbook .................................... 175 Ìåòîä Ìåòîä Ìåòîä Ìåòîä

SaveAs........................................................................................................... Activate .......................................................................................................... Close ............................................................................................................. Open .............................................................................................................

175 175 175 175

Ñâîéñòâî ActiveSheet ...................................................................................... 175 Ñâîéñòâî Name ........................................................................................................ 176

Ñâîéñòâî DisplayScrollBars: ïðèìåð ñîêðûòèÿ ïîëîñ ïðîêðóòêè ..................... 176 Ñâîéñòâî StatusBar ......................................................................................... 176 Óâåëè÷èòü ìàñøòàá àêòèâíîãî îêíà ................................................................ 176 Ñâîéñòâî FixedDecimal.................................................................................... 176 Ìåòîä Calculate .............................................................................................. 176 Ñåìåéñòâî Sheets ........................................................................................... 176 Ìåòîä Add ............................................................................................................... Âñòàâêà ëèñòà äèàãðàììû ........................................................................................ Ìåòîä Delete ............................................................................................................ Ñâîéñòâî Visible ....................................................................................................... Ìåòîä Move ............................................................................................................. Ìåòîä Copy..............................................................................................................

176 177 177 177 177 177

Ìåòîä Quit ...................................................................................................... 177

Ðàáîòà ñ ñîäåðæèìûì ðàáî÷åãî ëèñòà................................................ 177 Îáúåêò Range..................................................................................................... 177 Çàäàíèå äèàïàçîíà ........................................................................................ 177 Ñâîéñòâî Offset .............................................................................................. 178 Ñâîéñòâî Cells ................................................................................................ 178

Оглавление 11 Ñâîéñòâî EntireColumn ................................................................................... 178 Ñâîéñòâî EntireRow ........................................................................................ 178 Ñâîéñòâî UsedRange ...................................................................................... 179 Óäàëåíèå äèàïàçîíà ñî ñäâèãîì îñòàâøèõñÿ ÿ÷ååê âëåâî ............................. 179 Ìåòîä Insert: ïðèìåð âñòàâêè äèàïàçîíà ñî ñäâèãîì îñòàâøèõñÿ ÿ÷ååê âïðàâî ............................................................................................................ 179 Äîñòóï ê ñîäåðæèìîìó ÿ÷ååê è äèàïàçîíîâ ................................................... 179 Çàïèñü â ÿ÷ååêó çíà÷åíèÿ......................................................................................... 179

Ñâîéñòâî Formula ........................................................................................... 179 Ñâîéñòâî FormulaLocal.................................................................................... 179 Ñòèëü ññûëîê R1C1......................................................................................... 180 Ñâîéñòâî FormulaHidden ................................................................................. 180 Ñâîéñòâî Value ............................................................................................... 180 Ìåòîä AutoFill: àâòîçàïîëíåíèå äèàïàçîíà..................................................... 180 Ôîðìàòèðîâàíèå ÿ÷ååê .................................................................................. 180 Ñâîéñòâî ColumnWidth.................................................................................... 180 Ñâîéñòâî RowHeight ....................................................................................... 180 Ñâîéñòâî Font ................................................................................................. 180 Ñâîéñòâà Bold, Shadow, Name è Size......................................................................... 181

Îáúåêò Interior ................................................................................................ 181 Ñâîéñòâî Locked............................................................................................. 181 Ñâîéñòâî Style ................................................................................................ 181 Î÷èñòêà äèàïàçîíà ......................................................................................... 181 Îáúåêò Characters........................................................................................... 181

ÐÀÁÎÒÀ Ñ ÎÁÚÅÊÒÀÌÈ MS ACCESS ................................................................ 182 Ðàáîòà ñî ñòðóêòóðîé áàçû äàííûõ Access.......................................... 182 Îáúåêòû Database (áàçà äàííûõ), TableDef (îïðåäåëåíèå òàáëèöû) è ñåìåéñòâî Field (ïîëÿ òàáëèöû) ......................................................................... 182 Îáúÿâëåíèå ïåðåìåííûõ òèïà áàçà äàííûõ, îïðåäåëåíèå òàáëèöû è ïîëå òàáëèöû .......................................................................................................... 182 Ìåòîä OpenDatabase: îòêðûòèå áàçû äàííûõ................................................. 182 Ìåòîä CreateField îáúåêòà TableDef: äîáàâëåíèå òåêñòîâîãî ïîëÿ ................ 182 Ìåòîä Append................................................................................................. 183 Ìåòîä Close: çàêðûòèå áàçû äàííûõ .............................................................. 183 Ìåòîä CreateTable: ñîçäàíèå òàáëèöû ........................................................... 183 Ñâîéñòâà ïîëåé.................................................................................................. 184 Ââîä â ïîëå òàáëèöû çíà÷åíèÿ ïî óìîë÷àíèþ (DefaultValue) ........................ 184 Ñâîéñòâî Required: ïðîâåðêà äîïóñòèìûõ çíà÷åíèé ââîäèìûõ â ïîëå........... 184 Ñâîéñòâî Type: òèï äàííûõ............................................................................. 184 Ñâîéñòâî FieldSize: ðàçìåð ïîëÿ .................................................................... 184 Êîíñòàíòû òèïà äàííûõ ïîëÿ .......................................................................... 184

Ðàáîòà ñ ñîäåðæèìûì áàçû äàííûõ Access......................................... 184 Îáúåêò DoCmd ................................................................................................... 185 Ìåòîä OpenTable: îòêðûòèå òàáëèöû ............................................................. 185 Ìåòîä OutputTo: âûâîä òàáëèöû .................................................................... 185 Ìåòîä PrintOut: ïå÷àòü òàáëèöû ..................................................................... 185 Ìåòîä OpenForm: îòêðûòèå ôîðìû ................................................................ 185 Ìåòîä Maximize: ðàçâåðíóòü ôîðìó .......................................................................... 185 Ìåòîä Maximize: ñâåðíóòü ôîðìó äî çíà÷êà .............................................................. 185

Ìåòîä GoToRecord: íàâèãàöèÿ ôîðìû ........................................................... 185 Ìåòîä GoToControl: ïåðåäà÷à ôîêóñà ýëåìåíòó óïðàâëåíèÿ ôîðìû .............. 185 Ñâîéñòâî AllowDeletions: çàïðåò óäàëåíèÿ çàïèñåé ïîëüçîâàòåëåì ............... 186 Ñâîéñòâî AllowEdits: çàïðåò èçìåíåíèÿ çàïèñåé ............................................ 186 Ñâîéñòâà ôîðìû............................................................................................. 186 Ñåìåéñòâî Controls .................................................................................................. 186 Ñâîéñòâî CurrentRecord ........................................................................................... 186 Ñâîéñòâî Dirty .......................................................................................................... 186 Ñâîéñòâà FilterOn è Filter........................................................................................... 187 Ñâîéñòâî NavigationButtons....................................................................................... 187

Ñåìåéñòâî Forms ............................................................................................ 187 Ìåòîä Quit: çàêðûòèå áàçû äàííûõ ................................................................ 188

ÃËÀÂÀ 8

12 Оглавление VBA-ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ: ÏÐÈÌÅÐÛ È ÈËËÞÑÒÐÀÖÈÈ....................... 190 ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ ÎÁÚÅÊÒΠÈÍÒÅÃÐÈÐÎÂÀÍÍÎÉ ÑÐÅÄÛ MS OFFICE .................. 191 Ïàíåëè èíñòðóìåíòîâ è ìåíþ â ïðèëîæåíèÿõ MS Office....................... 191 Îáúåêòû òèïà CommandBar ............................................................................... 191 Îáúåêòû òèïà CommandBarControl ................................................................. 191 Ìåòîä FindControl: ïîèñê ýëåìåíòà óïðàâëåíèÿ ............................................. 191 Ñâîéñòâî FaceId: èçìåíåíèå çíà÷êà íà ýëåìåíòå óïðàâëåíèÿ........................ 192 Ñîçäàíèå ïîëüçîâàòåëüñêîé ïàíåëè èíñòðóìåíòîâ ........................................... 192 Ìåòîä Add ...................................................................................................... 192 Ñâîéñòâî Visible .............................................................................................. 192 Ñîçäàíèå íîâîé êíîïêè ..................................................................................... 193 Ñîçäàíèå ïîëüçîâàòåëüñêîãî ìåíþ ................................................................... 194 Äîáàâëåíèå ïîëüçîâàòåëüñêîé êîìàíäû â ñòàíäàðòíîå ìåíþ ïðèëîæåíèÿ MS Office............................................................................................................ 196

Ïðîãðàììèðîâàíèå ïîìîùíèêà Office................................................. 197 Îáúåêò Assistant ................................................................................................. 198 Îáðàáîòêà îøèáîê ñ èñïîëüçîâàíèåì îïåðàòîðà On Error GoTo ....................... 198 Ñîçäàíèå ïîëüçîâàòåëüñêîé Ñïðàâêè ................................................................ 198 Ïåðåìåííàÿ òèïà Balloon................................................................................ 199 Ñâîéñòâî Visible .............................................................................................. 199 Ñâîéñòâî Animation ......................................................................................... 199 Ìåòîä NewBalloon .......................................................................................... 199 Ñâîéñòâà Heading è Text ................................................................................. 199 Ñâîéñòâî Button.............................................................................................. 199 Ñâîéñòâî Labels .............................................................................................. 200 Ìåòîä Show .................................................................................................... 200 Ðåàêöèÿ ïîìîùíèêà íà âûáîð ïîëüçîâàòåëÿ .................................................. 201

ÔÎÐÌÀÒÈÐÎÂÀÍÈÅ ÎÁÚÅÊÒÎÂ ÏÐÈËÎÆÅÍÈÉ MS OFFICE ..................................... 202 Ïðèìåð ôîðìàòèðîâàíèÿ äîêóìåíòà Word .......................................... 203 Ôîðìàòèðîâàíèå àáçàöà.................................................................................... 203 Èñïîëüçîâàíèå êîíñòàíòû vbCr ...................................................................... 203 Èñïîëüçîâàíèå ìåòîäà InsertBefore................................................................ 204 Ñâîéñòâî Content............................................................................................ 204 Ñâîéñòâî Style ................................................................................................ 204 Çàãîëîâîê ñîîáùåíèÿ .............................................................................................. Ïðîñòîé òåêñò .......................................................................................................... Ñïèñêè ..................................................................................................................... Ïîäïèñü ...................................................................................................................

204 205 205 205

Èçìåíåíèå ðàçìåðà øðèôòà .......................................................................... 205 Îáùèé âèä äîêóìåíòà: ïðèìåð èçìåíåíèÿ ãðàíèö äîêóìåíòà (ðàçäåëà) ....... 206 Ñâîéñòâî ArtStyle: õóäîæåñòâåííûé ñòèëü ãðàíèöû .................................................... 207 Ñâîéñòâîì ArtWidth: øèðèíà ãðàíèöû ....................................................................... 207

Ãðàíèöû àáçàöà .............................................................................................. 207 Ñâîéñòâî SpaceAfter: îòñòóï ñíèçó ................................................................. 208 Ñâîéñòâî Alignment: ïðèìåð âûðàâíèâàíèÿ àáçàöà ñëåâà.............................. 208 Ôîðìàòèðîâàíèå ãðàôè÷åñêèõ îáúåêòîâ (ñåìåéñòâî Shapes) ........................... 208 Ìåòîä AddShape ............................................................................................. 208 Íàäïèñü íà àâòîôèãóðå .................................................................................. 209 Ôîðìàòèðîâàíèå äîêóìåíòà ïîñðåäñòâîì òåì .................................................. 209 Ìåòîä ApplyTheme.......................................................................................... 210

ÏÐÅÄÑÒÀÂËÅÍÈÅ ÄÀÍÍÛÕ Â MS OFFICE ........................................................... 210 Ïîäãîòîâêà òàáëè÷íûõ äàííûõ ............................................................ 210 Óñëîâíûå ôîðìàòû ............................................................................ 211

Ïîñòàíîâêà çàäà÷è ............................................................................................. 211 Îïðåäåëåíèå äèàïàçîíà óñëîâíîãî ôîðìàòèðîâàíèÿ........................................ 211 Îáúåêò FormatCondition...................................................................................... 211 Ìåòîä Add: äîáàâëåíèå óñëîâíîãî ôîðìàòà .................................................. 212 Ôîðìàòèðîâàíèå äàííûõ óäîâëåòâîðÿþùèõ óñëîâèþ........................................ 212 Äîáàâëåíèå âòîðîãî óñëîâíîãî ôîðìàòà........................................................... 212

Èçìåíåíèå ãðàíèö òàáëèöû ................................................................ 214

Оглавление 13 Äèàãðàììû ........................................................................................ 214 Îáúåêò ChartObject: âñòàâêà äèàãðàììû íà ëèñò Excel ...................................... 214 Ìåòîä SetSourceData: ñîçäàíèå îáû÷íîé ãèñòîãðàììû ................................. 215 Ñâîéñòâî HasLegend....................................................................................... 216 Ñâîéñòâî ChartType: çàäàíèå òèïà äèàãðàììû............................................... 216 Ìåòîä ApplyCustomType: ñîçäàíèå ïîëüçîâàòåëüñêîãî òèïà äèàãðàìì ......... 217 Ñâîéñòâî HasTitle: ñîçäàíèå çàãîëîâêà äèàãðàììû........................................ 218 Ñâîéñòâî ChartTitle ................................................................................................... 218

Ñåìåéñòâî Axes: ôîðìàòèðîâàíèå îñåé äèàãðàììû ...................................... 219 Ïðèìåðû ôîðìàòèðîâàíèÿ äèàãðàìì ................................................................ 219 Ñâîéñòâà HasTitle è AxisTitle: ñîçäàíèå çàãîëîâêîâ îñåé äèàãðàììû ............. 219 Ñâîéñòâà Elevation, Rotation è Perspective: ïîâîðîò äèàãðàììû âîêðóã îñåé 220 Èçìåíåíèå çàëèâêè îáëàñòè äèàãðàììû ........................................................ 221

ÃËÀÂÀ 9 ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈÅ Â OUTLOOK: ÄÎÊÓÌÅÍÒÎÎÁÎÐÎÒ È ÝËÅÊÒÐÎÍÍÀß ÏÎ×ÒÀ .............................................................................................. 223 ÝËÅÊÒÐÎÍÍÀß ÏÎ×ÒÀ  ÑÎÂÐÅÌÅÍÍÎÌ ÎÔÈÑÅ .................................................. 223 ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È ................................................................................. 223 ÎÁÚÅÊÒÍÀß ÌÎÄÅËÜ OUTLOOK 2000.............................................................. 224 Êëàññû è îáúåêòû .............................................................................. 225 Áèáëèîòåêè òèïîâ .............................................................................. 225 Êàê ïîäêëþ÷èòü ê Excel áèáëèîòåêó òèïîâ Outlook ............................................. 225

Íàçíà÷åíèå è îñíîâíûå ñâîéñòâà íåêîòîðûõ êëàññîâ Outlook .............. 227 Êëàññ Êëàññ Êëàññ Êëàññ Êëàññ Êëàññ

Application ................................................................................................ 228 NameSpace .............................................................................................. 228 SyncObject ............................................................................................... 229 Explorer .................................................................................................... 229 MAPIFolder ............................................................................................... 230 MailItem.................................................................................................... 230

ÑÎÇÄÀÍÈÅ Ó×ÅÒÍÛÕ ÇÀÏÈÑÅÉ ....................................................................... 231 ÑÎÇÄÀÍÈÅ ÏÐÈËÎÆÅÍÈß REGISTRATOR ........................................................... 234 Ñîçäàíèå çàãîòîâêè ïðèëîæåíèÿ ........................................................ 235 Èçìåíåíèå èìåíè ëèñòà .................................................................................... 235 Ñîçäàíèå ýëåìåíòà óïðàâëåíèÿ Êíîïêà............................................................. 235 Ñîçäàíèå çàãîòîâêè ìàêðîñà, ñâÿçàííîãî ñ êíîïêîé ......................................... 235 Ñîçäàíèå ìîäóëåé, êëàññîâ è ôîðì, è èçìåíåíèå èõ ñâîéñòâ .......................... 235 Èçìåíåíèå ñâîéñòâ êíîïêè íà ðàáî÷åì ëèñòå Excel .......................................... 237

Îïðåäåëåíèå ïðîãðàììíûõ ýëåìåíòîâ ............................................... 237 Àíàëèç îáúÿâëåííûõ ïðîãðàììíûõ ýëåìåíòîâ .................................................. 239

Ðàçðàáîòêà ñõåìû ïðèëîæåíèÿ ........................................................... 239 Êîìïèëÿöèÿ ïðîåêòà è çàïóñê åãî íà âûïîëíåíèå ............................................. 242

Íàïèñàíèå ïðîãðàììíîãî êîäà ........................................................... 243 Èíèöèàëèçàöèÿ è äåèíèöèàëèçàöèÿ .................................................................. 243 Ñèíõðîíèçàöèÿ .................................................................................................. 243 Îïðåäåëåíèå íóæíîãî èíòåðâàëà è ñîçäàíèå íîâîãî ëèñòà .............................. 247 Êëþ÷åâàÿ ïðîöåäóðà ïðèëîæåíèÿ — âûáîðêà âõîäÿùèõ è èñõîäÿùèõ ïèñåì .... 250 Äîâîäêà ïðèëîæåíèÿ Registration äî ïðîôåññèîíàëüíîãî óðîâíÿ ..................... 254

Äîâîäêà è ñîâåðøåíñòâîâàíèå ïðèëîæåíèÿ ........................................ 259 ÏÐÈËÎÆÅÍÈÅ VBA, ÊÀÊ ßÇÛÊ ÏÐÎÃÐÀÌÌÈÐÎÂÀÍÈß: ÄÀÍÍÛÅ, ÑÈÍÒÀÊÑÈÑ È ÔÓÍÊÖÈÈ ........................................................................................................ 262 ÄÀÍÍÛÅ VBA: ÒÈÏÛ ÄÀÍÍÛÕ, ÏÅÐÅÌÅÍÍÛÅ È ÊÎÍÑÒÀÍÒÛ ................................... 262 Ïåðåìåííûå è êîíñòàíòû ................................................................... 263 Êîíñòàíòû .......................................................................................................... 263 Îáúÿâëåíèå êîíñòàíò ..................................................................................... 263

14 Оглавление Ïåðåìåííûå....................................................................................................... 264 Îáúÿâëåíèå ïåðåìåííûõ ................................................................................ 264 Èíèöèàëèçàöèÿ ïåðåìåííûõ........................................................................... 264 Çà÷åì îáúÿâëÿòü ïåðåìåííûå? ...................................................................... 264 Äèðåêòèâà Option Explicit ................................................................................ 265 Îáëàñòü âèäèìîñòè è âðåìÿ æèçíè ïåðåìåííûõ ............................................ 266 Ìàññèâû ............................................................................................................ 267 Äèíàìè÷åñêèå ìàññèâû .................................................................................. 267 Îáúåêòíûå ïåðåìåííûå ..................................................................................... 268

Òèïû äàííûõ ...................................................................................... 269 Çà÷åì íóæíî îáúÿâëÿòü òèï? ............................................................................. 269 Ïðîñòûå òèïû äàííûõ ........................................................................................ 270 Boolean ........................................................................................................... 270 Integer............................................................................................................. 270 Long ................................................................................................................ 270 Single .............................................................................................................. 271 Double............................................................................................................. 271 Currency.......................................................................................................... 271 Date ................................................................................................................ 272 String .............................................................................................................. 272 Variant ............................................................................................................. 273 Òèïû äàííûõ, îïðåäåëÿåìûå ïîëüçîâàòåëåì..................................................... 273

ÑÈÍÒÀÊÑÈ×ÅÑÊÈÅ ÊÎÍÑÒÐÓÊÖÈÈ ßÇÛÊÀ VBA ................................................... 274 Âûðàæåíèÿ è îïåðàöèè ...................................................................... 274 Àðèôìåòè÷åñêèå îïåðàöèè ................................................................................ 274 Ëîãè÷åñêèå îïåðàöèè ........................................................................................ 275 Îïåðàöèè ñðàâíåíèÿ.......................................................................................... 275 Îïåðàöèÿ Is .................................................................................................... 275 Îïåðàöèÿ Like ................................................................................................. 276 Äèðåêòèâû Option Compare Binary è Option Compare Text .......................................... 276 Ïðèìåðû èñïîëüçîâàíèÿ îïåðàöèè Like .................................................................... 276

Îïåðàöèè ñî ñòðîêàìè....................................................................................... 277

Óïðàâëÿþùèå êîíñòðóêöèè ÿçûêà........................................................ 277 Ïåðåõîä ïî ìåòêå .............................................................................................. 277 Îïåðàòîð GoTo ............................................................................................... 277 Îïåðàòîð GoSub…Return ................................................................................ 278 Îïåðàòîðû öèêëà ............................................................................................... 278 Öèêë While … Wend ......................................................................................... 278 Öèêë Do … Loop.............................................................................................. 279 Öèêë For … Next.............................................................................................. 279 Öèêë For Each … Next ..................................................................................... 280 Ïðèíÿòèå ðåøåíèé: îïåðàòîðû âåòâëåíèÿ (óñëîâíûå êîíñòðóêöèè) .................. 280 Ôóíêöèÿ Switch ............................................................................................... 280 Ôóíêöèÿ Choose ............................................................................................. 281 Ôóíêöèÿ IIf ...................................................................................................... 281 Êîíñòðóêöèÿ If … Then … Else … End If ........................................................... 281 Êîíñòðóêöèÿ Select Case … End Select ........................................................... 282

ÎÏÅÐÀÒÎÐÛ È ÂÑÒÐÎÅÍÍÛÅ ÔÓÍÊÖÈÈ ßÇÛÊÀ VBA ............................................. 282 Èíòåðôåéñ ñ ïîëüçîâàòåëåì............................................................... 283 Âûâîä ñîîáùåíèé (ôóíêöèÿ MsgBox) ................................................................ 283 Ââîä äàííûõ (ôóíêöèÿ InputBox) ........................................................................ 284

Âçàèìîäåéñòâèå ñ îïåðàöèîííîé ñèñòåìîé ........................................ 285 Çàïóñê ïðèëîæåíèé (ôóíêöèÿ Shell) ................................................................... 285 Êàê ñäåëàòü çàïóùåííîå ïðèëîæåíèå àêòèâíûì (îïåðàòîð AppActivate) ........... 286 Óïðàâëåíèå äðóãèì ïðèëîæåíèåì ïðè ïîìîùè èìèòàöèè ââîäà ñ êëàâèàòóðû (îïåðàòîð SendKeys).......................................................................................... 286 Âû÷èñëåíèå ôàêòîðèàëà ïðè ïîìîùè êàëüêóëÿòîð Windows èç VBA-êîäà...... 286 Ôàéëîâûå îïåðàöèè .......................................................................................... 287 Êàêèå áûâàþò ôàéëû ...................................................................................... 288 Ôàéëû ïîñëåäîâàòåëüíîãî äîñòóïà .......................................................................... 288 Ôàéëû ïðîèçâîëüíîãî äîñòóïà ................................................................................. 288

Оглавление 15 Áèíàðíûå (äâîè÷íûå) ôàéëû .................................................................................... 288

Îïåðàöèè ñ êàòàëîãàìè è äèñêàìè ................................................................. 288 Îïåðàòîð ChDrive ..................................................................................................... Îïåðàòîð MkDir........................................................................................................ Îïåðàòîð ChDir ........................................................................................................ Îïåðàòîð RmDir ....................................................................................................... Ôóíêöèÿ CurDir......................................................................................................... Ôóíêöèÿ Dir..............................................................................................................

288 288 288 289 289 289

Ïåðåèìåíîâàíèå ôàéëà èëè êàòàëîãà (îïåðàòîð Name) ................................ 290 Êîïèðîâàíèå ôàéëîâ (îïåðàòîð FileCopy) ...................................................... 290 Óäàëåíèÿ ôàéëîâ (îïåðàòîð Kill) .................................................................... 290 Êàê îòêðûòü ôàéë (îïåðàòîð Open) ................................................................ 291 Îñîáåííîñòè ðåæèìà ïðîèçâîëüíîãî äîñòóïà ê ôàéëó .............................................. 291

Ââîä äàííûõ èç îòêðûòîãî ôàéëà (îïåðàòîðû Input, Line Input) ..................... 292 Âûâîä äàííûõ â îòêðûòûé ôàéë (îïåðàòîðû Write, Print) ............................... 293 Âûâîä ñòðîê â òåêñòîâûé ôàéë ...................................................................... 293 Îïåðàòîð Width ........................................................................................................ 293 Ôóíêöèÿ Spc ............................................................................................................ 293 Ôóíêöèÿ Tab ............................................................................................................ 294

Çàêðûòèå ôàéëîâ (îïåðàòîðû Reset, Close) ................................................... 294 Îïåðàöèè ñ ôàéëàìè, îòêðûòûìè â ðåæèìå ïðîèçâîëüíîãî äîñòóïà ............. 294 Îïåðàòîð Seek ......................................................................................................... Ôóíêöèÿ Loc............................................................................................................. Îïåðàòîð Put ........................................................................................................... Îïåðàòîð Get ........................................................................................................... Ôóíêöèÿ EOF ............................................................................................................ Îïåðàòîð Lock ......................................................................................................... Îïåðàòîð Unlock ......................................................................................................

294 294 295 295 295 295 295

Ðàáîòà ñ ñèñòåìíûì ðååñòðîì Windows ............................................................ 296 Îïåðàòîð SaveSetting ............................................................................................... 296 Ôóíêöèÿ GetSetting................................................................................................... 296 Îïåðàòîð DeleteSetting............................................................................................. 297

Ðàáîòà ñî çíà÷åíèÿìè äàòû è âðåìåíè ............................................................. 297 Ïðåäñòàâëåíèå çíà÷åíèé äàòû è âðåìåíè â MS Office è VBA......................... 297 Ïðåäñòàâëåíèå çíà÷åíèé âðåìåíè............................................................................ 297 Ïðåäñòàâëåíèå çíà÷åíèé äàòû.................................................................................. 297

Ôóíêöèÿ DateSerial ......................................................................................... 297 Ïðåîáðàçîâàíèå ñòðîê â çíà÷åíèÿ äàòû (ôóíêöèÿ DateValue) ........................ 298 Ôóíêöèÿ TimeSerial ......................................................................................... 298 Ïðåîáðàçîâàíèå ñòðîê â çíà÷åíèÿ âðåìåíè (ôóíêöèÿ TimeValue) .................. 299 Ñèñòåìíàÿ äàòà è ñèñòåìíîå âðåìÿ ............................................................... 299 Ôóíêöèÿ Date ........................................................................................................... 299 Ôóíêöèÿ Time ........................................................................................................... 299 Ôóíêöèÿ Now............................................................................................................ 300

Êàëåíäàðíûå âû÷èñëåíèÿ ............................................................................... 300 Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ

DateAdd...................................................................................................... DateDiff ...................................................................................................... DatePart ..................................................................................................... Year ........................................................................................................... Month......................................................................................................... Day ............................................................................................................ Weekday .....................................................................................................

300 301 301 302 302 302 302

Âû÷èñëåíèÿ ñî çíà÷åíèÿìè âðåìåíè.............................................................. 303 Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ Ôóíêöèÿ

Hour ........................................................................................................... Minute ........................................................................................................ Second ....................................................................................................... Timer..........................................................................................................

303 303 303 303

Ðàáîòà ñî ñòðîêàìè ........................................................................................... 303 Ñðàâíåíèå ñòðîê (ôóíêöèÿ StrComp).............................................................. 303 Âûäåëåíèå ïîäñòðîêè .................................................................................... 304 Ôóíêöèè Left è LeftB ................................................................................................. 304 Ôóíêöèè Right è RightB............................................................................................. 304 Ôóíêöèè Mid è MidB ................................................................................................. 304

Ïîèñê â ñòðîêå (ôóíêöèè InStr, InStrB) ........................................................... 305 Ïðåîáðàçîâàíèÿ ðåãèñòðà (ôóíêöèè LCase, UCase) ....................................... 305 Ôîðìàòèðîâàíèå ñòðîêè................................................................................. 306 Ôóíêöèÿ Space ......................................................................................................... 306

16 Оглавление Ôóíêöèÿ String ......................................................................................................... 306 Ôóíêöèÿ Len............................................................................................................. 306 Ïðèìåðû ôîðìàòèðîâàíèÿ ñòðîê .............................................................................. 306 Ôóíêöèè óäàëåíèÿ ïðîáåëîâ (LTrim, RTrim, Trim) ...................................................... 307 Ôóíêöèÿ Format ........................................................................................................ 307

Ïðåîáðàçîâàíèå ñòðîêîâûõ è ñèìâîëüíûõ çíà÷åíèé...................................... 309

Ôóíêöèè Asc, AscB è AscW ....................................................................................... 309 Ôóíêöèè Chr, Chr B è Chr W ..................................................................................... 309

Ïðèìåðû èñïîëüçîâàíèÿ ñòðîêîâûõ ôóíêöèé................................................. 309 Øèôðîâàíèå/äåøèôðîâàíèå ñòðîêè ........................................................................ 309 Ðàçáîð ñòðîêè.......................................................................................................... 311

Предисловие Çà÷åì ýòà êíèãà Современный офис, будь то государственное или коммунальное учреждение, торговая фирма или заводоуправление, немыслим без пакета программ MS Office. Повсюду, где ведется деловая переписка, и работают с текстовыми документами любого рода, можно увидеть вездесущий Word. На листах рабочих книг Excel ведется множество видов учета и отчетности — эта программа распространена почти так же широко, как и Word. Практически говоря, MS Word и MS Excel стали стандартом “де-факто” в офисной деятельности. Такие приложения пакета Office, как MS Access и MS PowerPoint, используются не столь повсеместно, но все же распространены достаточно широко. И столь же широко, как сам пакет MS Office, распространено его незнание. Тысячи и тысячи пользователей могли бы несказанно облегчить и усовершенствовать свою работу, использовав лишь маленькую толику возможностей MS Office, но… не делают этого. Такой пользователь готов часами выполнять утомительные, рутинные операции. Идея автоматизировать свою работу ему в голову не приходит, — нет, не потому, что он не хотел бы облегчить себе жизнь. И не потому, что мощь MS Office ему неведома, — он слышал такие слова, как “макрос”, “VBA”, “OLE” и т.п. Но эти слова его слегка пугают. “Это, пожалуй, сложновато для меня”. “Это для программистов”. Но немногие отдают себе отчет в том, что MS Office — это не система программирования и не платформа для разработки программного обеспечения. MS Office — это пользовательский пакет и большинство имеющихся в нем средств предназначено для самых что ни на есть обыкновенных пользователей-непрофессионалов. В этой книге будет показано, как можно решать типичные задачи современного офиса, используя, если не все, то многие из возможностей пакета MS Office, которые обычно остаются “за кадром”. Здесь будет показано, каким образом даже не слишком продвинутый пользователь в состоянии значительно облегчить себе жизнь и усовершенствовать свою работу при помощи инструментальных средств пакета MS Office.

Ñòàðèííûé ñïîñîá: “Äåëàé, êàê ÿ!” В стародавние времена, на военном флоте для связи между кораблями использовали специальные сигналы — наборы вымпелов. Когда флагманский фрегат, в разгаре сражения, не мог или не успевал сообщить своим кораблям детальные инструкции по предстоящему маневрированию, он поднимал сигнал “Делай, как я!”. И начинал маневр, показывая собственным примером, что и как следует делать остальным кораблям эскадры. Этот старинный способ “сверхбыстрого обучения” представляется наиболее подходящим для того, чтобы ввести читателя-непрофессионала во внутренний мир Office. В этой книге решаются типичные задачи современного офиса, не вдаваясь в слишком подробные объяснения. Поначалу, неподготовленному читателю будет непонятным абсолютно все — он должен будет просто выполнять детальные инструкции и наблюдать, что получается в результате. Выполняемые действия будут снабжены некоторыми объяснениями, из которых, по мере продвижения вперед по материалу книги, картина будет становиться все яснее. Сначала будет понятным только кое-что из выполняемых действий, затем станет понятным многое. Наконец, в последней главе будут суммированы и систематизированы те основы макроязыка VBA, которыми на практике к тому моменту уже овладеет читатель.

18 Предисловие

Î ÷åì ýòà êíèãà Среди великого многообразия “офисных” задач были выбраны некоторые, наиболее типичные и более всего, на наш взгляд, нуждающиеся в автоматизации. Вообще говоря, задачи автоматизации можно разделить на два больших класса. К первому из них относятся операции, которые, в принципе, можно без большого напряжения выполнить и вручную — просто придется много раз нажимать на клавиши или “до одурения” щелкать мышью. При автоматизации таких операций все сводится к тому, чтобы заменить много щелчков мышью или нажатий на клавиши одним щелчком или одним нажатием. Задачи второго класса сложнее. Есть операции, которые, даже запасясь бесконечным терпением, вручную не выполнишь. Решать такие задачи труднее, но и результат решения более ценен. Этот результат — новое качество, новые функциональные возможности, которые ранее были недоступны. Понятно, что ради решения в первую очередь именно таких задач стоит браться за изучение инструментальных средств Office.

×òî ïîòðåáóåòñÿ äëÿ ðàáîòû ñ ýòîé êíèãîé Прежде всего, — желание. Желание работать в современном офисе на современном уровне. Этому не так трудно научиться, как некоторые думают, но определенных усилий это все же потребует. Конечно же, нам потребуется пакет MS Office, установленный на компьютере. Должен ли это быть именно MS Office 2000? Не обязательно (хотя, разумеется, желательно). Большинство средств, которые будут использованы, имеются и в предыдущих версиях пакета. Если, например, говорить о пакете MS Office 97, то практически все, о чем пойдет речь в этой книге, совместимо с ним. Существует несколько вариантов установки MS Office. Может встретиться вариант, в котором отсутствуют некоторые из инструментальных средств. Например, возможна установка пакета MS Office без VBA — системы поддержки языка программирования Visual Basic для приложений. В каких-то случаях, возможно, необходимо будет доустановить недостающие компоненты пакета.

Î ìàêðîñàõ è ìàêðîâèðóñàõ В пакете MS Office 2000 (в отличие от предыдущих версий) предусмотрена многоуровневая защита от макровирусов. Что такое макровирус? Чтобы понять это, необходимо вначале иметь представление о “нормальных макропрограммах” — макросах, как их принято называть. Макроязык VBA — Visual Basic для приложений — является, фактически, одним из основных связующих звеньев, благодаря которым различные программы пакета MS Office связаны воедино, и благодаря которым вообще можно говорить о таких вещах, как рабочая среда MS Office или объекты Office.

Îïàñíû ëè ìàêðîñû? Опасны ли макросы сами по себе? Нет, конечно. Не более чем опасны обычные программы, такие, например, как MS Word или MS Excel. Подобно тому, как существуют программные вирусы, способные “заразить” программный файл (например, исполняемый файл MS Word) точно так же существуют и макровирусы, способные “заразить” макропрограмму.

Ãäå îíè íàõîäÿòñÿ? Макропрограммы — макросы — пакета MS Office “живут” в документах Word, шаблонах, листах рабочих книг Excel и других файлах данных Office. Если макрос базы данных Access может физически находиться только в файле базы данных, то макросы Word обитают как в собственно документах (при этом, передав кому-то файл документа, одновременно будет передан содержащиеся в нем макросы), так и файлах шаблонов — в первую очередь в общем шаблоне Normal

Предисловие 19 (Обычный). Содержащиеся в шаблоне Normal макросы никуда не передаются при пересылке документов и всегда “под рукой”, то есть они связаны с данным конкретным компьютером. В среде Excel макросы могут содержаться как в рабочих книгах (и тогда они передаются вместе с файлом рабочей книги), так и в шаблонах Excel.

Íàäî ëè áîÿòüñÿ ìàêðîñîâ? Если будет открыт документ Word, присланный, например, по электронной почте, то риск поражения макровирусами лежит полностью на пользователе, сделавшем это. Если используются собственные макросы, записанные в общем шаблоне Normal, и не открываются чужие документы с макросами, то макровирусы документов Word гарантированно не страшны. Отношение к макровирусам напоминает отношение к автомобильным авариям. Несомненным фактом является то, что в дорожных происшествиях гибнут ежегодно тысячи и десятки тысяч людей. Но никому не приходит при этом в голову отказаться от автомобилей, как от средства передвижения. И случающиеся в жилых зданиях пожары не заставили еще человечество вернуться в пещеры. Слишком многое дают людям автомобильный транспорт и жилые здания — достаточно много, чтобы человечество было согласно идти на риск несчастных случаев. Точно так же, макросы рабочей среды MS Office слишком многое дают пользователю, чтобы избегать угрозы макровирусов ценой отказа от услуг “добропорядочных” макросов. Как и в примерах с автомобилями и пожарами, здесь, чтобы спать спокойно, достаточно соблюдать свои “правила дорожного движения” и “правила пожарной безопасности”.

Óðîâíè áåçîïàñíîñòè â Office 2000 Диалоговое окно Áåçîïàñíîñòü (рис. 1) позволяет задать реакцию приложения на наличие макросов в открываемом документе — новация версии 2000 — команду Ñåðâèñ | Ìàêðîñ | Áåçîïàñíîñòü). Варианты “Высокая” и “Низкая” соответствуют крайним степеням, нас же интересует “золотая середина” — этот уровень безопасности предполагает, что приложение не будет открывать содержащий макросы документ без подтверждения со стороны пользователя. Если открываемый документ (например, рабочая книга Excel) содержит макросы и если, при этом, в данном приложении Office выбран “средний” уровень безопасности, на экране появится диалоговое окно с предупреждением. Если это документ неизвестного вам происхождения, полученный из источника, вирусная чистота которого с вашей точки зрения не гарантирована, то разумней всего будет, конечно, отключить макросы. Но если речь идет о собственном документе Рис. 1. Диалоговое окно Безопасность пользователя или рабочей книге, которую он сам снабдил макросами VBA, то нет причин отказываться от полезных (а порою — просто необходимых) функциональных возможностей, которые связаны с применением макросов VBA.

В. Д. Хорев

Глава 1 Excel: cîçäàíèå ïðàéñ-ëèñòà ×òî ìîæåò áûòü ïðîùå ïðÿìîóãîëüíîé òàáëèöû? Начнем с решения самых простых задач. Очень часто возникает необходимость предоставить потенциальному клиенту прейскурант — список товаров с их ценами, которые фирма предлагает к продаже на тех или иных условиях. Такой список в наше время называют прайс-листом или просто прайсом. Итак, создадим прайс-лист фирмы “Универсал”. Простейший прайс-лист представляет собой прямоугольную таблицу, где в двух столбцах расположены наименования различных товаров и соответствующие им цены. Ничто не мешает нарисовать подобную таблицу от руки или создать ее в документе Word, однако чаще всего прайс-лист выполняют в форме таблицы Excel. Тому есть много причин, но главная из них заключается в том, что работать с табличными данными легче всего именно на листе рабочей книги Excel. Для начала будет создан простейший прайс-лист (проще не бывает), затем он будет постепенно совершенствоваться, расширяя свои функциональные возможности и облегчая работу с ним.

Òîâàðû è öåíû “â ñòîëáèê” Предположим, что одна из граней деятельности нашей фирмы — продажа комплектующих и расходных материалов для офисной техники. Предположим далее, что нам необходимо представить потенциальным клиентам прайс-лист по расходным материалам − пусть среди наших товаров к этой категории относятся картриджи для струйных, лазерных и матричных принтеров. Это обычные в современном офисе расходные материалы, они существуют и применяются в очень широком ассортименте, и нет принтера, для которого не приходилось бы время от времени покупать новый картридж. Другой вид расходного материала, который предлагается — бумага для принтеров и копировальной техники. Без бумаги, несмотря на наступление “эры безбумажных технологий”, также не обойтись в современном офисе. Таким образом, наш прайс должен будет состоять из нескольких разделов. Создадим вначале простейший прайс-лист для продажи бумаги в ассортименте. Для этого достаточно открыть рабочую книгу Excel и ввести “в столбик” наименования марок бумаги и розничные цены, по которым эта бумага предлагается. Любой человек, хотя бы один раз, в жизни, работавший в среде Excel, сделает это без труда, как показано на рис. 1.1. Все, что требуется, это ввести текстовые и цифровые данные в столбцы A и B, а также увеличить ширину столбца A так, чтобы самое длинное наименование было видно полностью. Для этого достаточно захватить мышью границу столбцов между серыми заголовками A и B, и перетащить ее в нужном направлении. Прежде чем дополнить таблицу разделом “Картриджи”, необходимо каким-то образом оформить уже введенные данные с тем, чтобы они выглядели, как самостоятельная структурная единица. Прежде всего, посмотрим на столбец B — поскольку в него вводятся денежные значения, его необходимо отформатировать соответствующим образом (рис. 1.2). Если число обозначает денежную сумму, то это не просто число. Для ввода таких данных существуют специальные правила, а для корректного выполнения арифметических операций с ними Excel должен использовать специальную “денежную” арифметику.

22 Глава 1. Excel: cоздание прайс-листа

Рис. 1.1. Простейший прайс-лист

Êàê îòôîðìàòèðîâàòü ñòîëáåö äëÿ ââîäà äåíåæíûõ çíà÷åíèé 1. 2. 3. 4. 5. 6.

Выделите необходимый столбец (в нашем случае это столбец B) щелчком мыши на его заголовке (области серого цвета с буквой “B”, в верхней части листа). При этом все ячейки, принадлежащие к столбцу, окажутся выделенными. Выполните команду Формат | Ячейки. На вкладке Число выберите в списке Числовые форматы пункт Денежный В поле Число десятичных знаков установите при помощи кнопки-счетчика значение 2. В раскрывающемся списке Обозначение выберите обозначение необходимой денежной единицы. Например, р. Русский для российского рубля или грн. Ukrainian для украинской гривны. Щелкните на кнопке OK. Ячейки столбца B примут вид, как на рис. 1.2.

Рис. 1.2. Ячейки столбца в формате Денежный Затем нам следует выделить раздел “Бумага” для того, чтобы вообще можно было говорить о разделах. Если продолжать ввод остальных данных далее “в столбик”, прайс-лист будет трудно читать и анализировать. Средства MS Office позволяют решить эту задачу множеством способов, выберем один из самых простых. Чтобы оформить раздел, для улучшения визуального восприятия прайс-листа, можно ячейки этого раздела, предварительно выделив, очертить жирными линиями при помощи команды Ôîð-

Что может быть проще прямоугольной таблицы? 23 ìàò | ß÷åéêè. В отобразившемся окне Ôîðìàò ÿ÷ååê на вкладке Ãðàíèöà выберите жирную линию в поле òèï ëèíèè, и щелкните на кнопке âíåøíèå, а затем — âíóòðåííèå. Результат форма-

тирования показан на рис. 1.3. Ячейки столбца B, которые используются для ввода денежных значений, можно выделить цветом (рис. 1.3) при помощи вкладки Âèä все того же диалогового окна Ôîðìàò ÿ÷ååê.

Рис. 1.3. Обрамление ячеек и изменение их цвета Наконец, созданный раздел необходимо снабдить заголовком. Для этого следует вставить новую строку, выполнить в ней объединение (слияние) ячеек и отформатировать получившуюся объединенную ячейку так, чтобы она “бросалась в глаза” (рис. 1.3).

Ñîçäàíèå çàãîëîâêà ðàçäåëà 1. 2. 3. 4. 5. 6. 7. 8.

Выделите любую ячейку в верхней строке таблицы и выполните команду Вставка | Строки. Введите в первую ячейку вставленной строки текст заголовка (“Бумага для принтеров”). Выделите первые две ячейки во вставленной пустой строке и выполните команду Формат | Ячейки. На вкладке Выравнивание диалогового окна Формат ячеек установите флажок объединение ячеек. В списках по горизонтали и по вертикали выберите пункты по центру. На вкладке Шрифт задайте параметры шрифта для заголовка. При помощи поля Цвет на вкладке Вид выделите заголовок цветом (цвет шрифта можно изменить на вкладке Шрифт). Щелкните на кнопке OK.

ÏÐÈÌÅ×ÀÍÈÅ

Форматировать заголовок выделенной области таблицы следует таким образом, чтобы объединенная ячейка с выравниванием по центру наилучшим образом подчеркивала единство находящихся под ней столбцов. Однако, выполнение операций с объединенными ячейками различно для разных версий Excel. Далее потребуется выполнять некоторые из таких операций, и, чтобы не усложнять данную задачу дополнительными мерами по совместимости с разными версиями Excel, постараемся больше не использовать объединенные ячейки. Таким образом, четвертый пункт предыдущей инструкции можно не выполнять.

Ôîðìàòèðîâàíèå äèàïàçîíà ÿ÷ååê ïî îáðàçöó Итак, первый раздел прайс-листа готов. Теперь можно приступить к созданию следующего раздела. Пусть это будет раздел, посвященный картриджам для струйных принтеров. Как и в любом другом деле, здесь самым трудным было начало. Продолжить “строительство” прайса уже гораздо легче.

24 Глава 1. Excel: cоздание прайс-листа Чтобы отформатировать новый раздел, можно использовать форматирование уже созданного раздела.

Êàê ôîðìàòèðîâàòü ðàçäåë ïðè ïîìîùè êîìàíäû Ñïåöèàëüíàÿ âñòàâêà 1. 2.

3. 4. 5. 6.

Выделите полностью (вместе с заголовком), раздел “Бумага”. Скопируйте его в буфер обмена Windows (Clipboard), выполнив команду Правка | Копировать. Теперь можно было вставить его в новом месте при помощи команды Правка | Вставить того же меню, и при этом получить точную копию раздела “Бумага”. Но в данном случае требуется только форматирование ячеек этого раздела, а не его содержимое. Поэтому, выполните следующие действия. Укажите начало форматирования. Для этого достаточно выделить одну ячейку. В данном примере это ячейка A11. Выполните команду Правка | Специальная Рис. 1.4. Диалоговое окно Специальная вставка. вставка В открывшемся диалоговом окне в группе переключателей Вставить установите переключатель форматы, как показано на рис. 1.4. По умолчанию установлен переключатель все. Щелкните мышью на кнопке OK. Excel отформатирует прямоугольную область в указанном месте (рис. 1.5) точно таким же образом, как это было сделано вручную с первым разделом прайс-листа — “Бумага”.

Рис. 1.5. Форматирование диапазона ячеек по образцу Теперь в новый раздел можно ввести данные — наименования картриджей для струйных принтеров и их цены. Поскольку количество элементов списка вряд ли совпадет с количеством строк в разделе, который использовался в качестве образца, размер нового раздела придется, вероятно, изменить.

Что может быть проще прямоугольной таблицы? 25

Óäàëåíèå è âñòàâêà ñòðîê Для удаления лишних строк их следует выделить при помощи мыши. Используйте для этого заголовки строк — цифры на сером фоне в левой части листа. 2. Выделенные строки удаляются командой Ïðàâêà | Óäàëèòü. 1.

ÏÐÈÌÅ×ÀÍÈÅ

Обратите внимание, нажатием клавиши [Delete] строки удалить не удастся. В этом случае будут удалены лишь данные, находящиеся в их ячейках. Для добавления строк используют команду Âñòàâêà | Ñòðîêè.

Ìåõàíèçìîì ãðóïïèðîâàíèÿ äàííûõ Действуя описанным выше методом форматирования диапазона и удаления/вставки строк, прайс-лист был дополнен еще несколькими разделами (рис. 1.6). Картриджи для лазерных и матричных принтеров представляются вполне обоснованным продолжением темы расходных материалов.

Рис. 1.6. Теперь разделов стало два Продолжая в том же духе, сотрудник, создающий прайс-лист своей фирмы, рано или поздно столкнется с одной проблемой. Очень хорошо, когда список разбит по разделам, в такой структуре гораздо легче ориентироваться. А если разделы разбиты на подразделы, тем лучше, так как в таком прайс-листе потенциальный покупатель быстрее найдет интересующее его наименование товара. Но как перемещаться по всем этим разделам и подразделам? При достаточно большом размере прайс-листа навигация по нему может быть затруднена. В рабочей среде Excel можно воспользоваться механизмом группирования данных, который позволяет структурировать таблицу по иерархическому принципу. Чтобы сгруппировать данные, в рассматриваемом примере, по отношению к каждому из созданных разделов необходимо выполнить следующую последовательность действий.

Êàê ãðóïïèðîâàòü äàííûå 1.

Выделите все строки раздела кроме его заголовка.

26 Глава 1. Excel: cоздание прайс-листа 2. 3.

Выполните команду Äàííûå | Ãðóïïà è ñòðóêòóðà | Ãðóïïèðîâàòü. Выделенные строки сразу будут сгруппированы.

ÏÐÈÌÅ×ÀÍÈÅ

Если были выделены не целые строки при помощи их заголовков, а просто группа ячеек, то отобразится диалоговое окно, в котором пользователю будет предложено уточнить, что он группирует — ñòðîêè или ñòîëáöû. Выполнив эти действия для каждого из разделов, будет получен прайс-лист из набора сворачивающихся списков, в котором каждый раздел может быть представлен как целиком, так и всего лишь одной строкой — его заголовком, как показано на рис. 1.7.

Рис. 1.7. Список структурирован: каждый из его разделов можно свернуть или развернуть щелчком на кнопке “—“ или “+“ В левой части рабочего листа теперь появилась дополнительная панель с кнопками “+” и “−”, которые служат для свертывания и развертывания соответствующих разделов. Кроме этого, кнопки 1 и 2 в левом верхнем углу листа позволяют свернуть, или, наоборот, развернуть все разделы одновременно. ÏÐÈÌÅ×ÀÍÈÅ

Уже сгруппированные данные можно еще раз сгруппировать, создав более высокий уровень иерархии. Для этого необходимо снабдить несколько разделов общим заголовком, выделить все строки за исключением этого заголовка и снова выполните команду Äàííûå | Ãðóïïà è ñòðóêòóðà | Ãðóïïèðîâàòü. При этом появится еще одна ступень группировки данных на панели в левой части листа, например, кнопка 3. В данном примере этого делать не надо, поскольку “тренировочный” прайс-лист недостаточно велик. Но если бы кроме расходных материалов в нем имелись другие типы товаров, то было бы логичным объединить все четыре раздела в один большой раздел, который мог бы сворачиваться и разворачиваться наряду с такими разделами, как, например, “комплектующие”, “офисная техника” и т.п.

Çàãîëîâîê ïðàéñ-ëèñòà Чтобы снабдить прайс-лист общим “фирменным” заголовком, потребуется вставить две строки и объединить полученные ячейки A1, A2, B1 и B2, как показано на рис. 1.8.

Как отделить внутреннюю информацию от внешней информации 27

Рис. 1.8. Прайс с общим заголовком

Êàê îòäåëèòü âíóòðåííþþ èíôîðìàöèþ îò âíåøíåé èíôîðìàöèè Созданный прайс-лист в предыдущем разделе, вполне пригоден для передачи его клиенту или контрагенту — в виде твердой копии, то есть отпечатанной на бумаге таблицы, или в качестве файла на дискете, или же в форме сообщения электронной почты. Но работать с ним в офисе фирмы вряд ли удобно. Ведь существуют такие реалии нынешней экономической жизни, как меняющийся валютный курс, который вынуждает оперативно менять цены на весь ассортимент предлагаемых фирмой товаров. Кроме того, в условиях рыночной экономики, каждая фирма разрабатывает и применяет различные ценовые стратегии. Это может выражаться в гибкой системе оптовых скидок, предполагающей динамическое изменение цен на товар в зависимости от условий продажи товара. Наконец, кроме проблем, связанных с оперативным ценообразованием, существует такой фактор, постоянно подвергающийся изменению, как наличие конкретного товара на складе. Причем под складом может подразумеваться как собственный склад фирмы, так и склад поставщика продукции или даже нескольких поставщиков. Все это оказывает влияние на текущее состояние прайс-листа. И было бы просто смешно не использовать здесь возможности Excel. Но должен ли клиент, в полученном текущем прайсе, видеть базовую цену в валюте или цену поставщика? Следует ли ему знать о наличии товара на складе? А как быть с товаром, которого в данный момент на складе вообще нет, но который уже завтра снова поступит на склад. Этого товара не должно быть и в текущей версии прайс-листа, но не удалять же, в самом деле, его строку из списка каждый раз с тем, чтобы назавтра ее снова приходилось создавать заново.

Ïîñòàíîâêà çàäà÷è Все это приводит к простой мысли, что прайс-листов у фирмы должно быть два. Второй из них только что был создан, но на самом деле это всего лишь “клиентский” прайс-лист. К “внутрифирменному” прайс-листу он имеет примерно такое же отношение, как и моментальная фотография бегущей лошади (допустим, вид слева) к самой лошади. Конечно же, “клиентский” прайс-лист должен каким-то образом получаться из “внутрифирменного” прайса, но при этом в него должны попадать отнюдь не все данные. Подумаем, в чем могут состоять отличия “внутрифирменного” прайс-листа и что нам необходимо добавить к своему “учебно-тренировочному” прайсу. В первую очередь необходим меха-

28 Глава 1. Excel: cоздание прайс-листа низм ценообразования, который автоматически формировал бы столбец розничных цен из некоторых данных. Часто используют следующий механизм. 1. Задается некая “базовая” цена, которая может представлять собой цену соответствующего товара в твердой валюте или же цену поставщика. 2. Из базовой цены, путем умножения на некий коэффициент, получают оптовую цену. Назовем этот коэффициент валютным курсом, хотя на практике он может и не иметь отношения к реальному валютному курсу. 3. Умножением оптовой цены на коэффициент розницы получают розничную цену. В реальности все это может выглядеть сложнее. Например, фирмы часто используют несколько оптовых коэффициентов для различных размеров партии поставки и для клиентов с разной предыдущей историей покупок, пользующимися, так называемыми, накопительными скидками. Для начала такой арифметики вполне достаточно. Справа от общего заголовка (то есть объединенных ячеек A1 и A2) есть подходящее место для размещения коэффициентов. Конечно, можно было бы просто ввести значение курса и коэффициента розницы в какие-нибудь ячейки и затем использовать ссылки на эти ячейки в формулах. Более правильный путь состоит в том, чтобы присвоить ячейкам имена и уже их использовать в формулах — тогда смысл каждой формулы будет понятен без дополнительных комментариев. Такой подход позволяет легко разобраться с механизмом ценообразования не только его разработчику, но и другим служащим фирмы.

Èìåíîâàííûå ÿ÷åéêè В данном примере необходимо выделить ячейку D1, и, щелкнув мышью на поле имени (рис. 1.9), ввести в него вместо D1 слово “КУРС”, завершив ввод нажатием клавиши [Enter]. Теперь во всех формулах “КУРС” будет означать данные из ячейки D1. Аналогичным образом необходимо присвоить имя “РОЗНИЦА” ячейке D2. Эти две ячейки можно выделить Рис. 1.9. Поле имени расположено в цветом, а чтобы назначение их было понятным, ввести левом верхнем углу рабочего листа, строки “КУРС=” и “РОЗН=” в пару ячеек слева от них, то где отображается адрес текущей есть в ячейки C1 и C2, которые также можно выделить ячейки цветом. Наконец, необходимо отформатировать столбцы C и D для ввода денежных значений. Столбец C нужно отформатировать, как столбец B, а для столбца D можно выбрать денежную единицу знак доллара. В строке 3 можно добавить заголовки для новых столбцов, отформатированные так же, как и заголовок столбца “Цена”: столбцу базовой цены пусть соответствует заголовок “База”, а столбцу оптовой цены — заголовок “Опт”, как показано на рис. 1.10.

Рис. 1.10. Отформатировать ячейки заголовков “Опт“ и “База“ проще всего при помощи копирования заголовка “Цена“ ÏÐÈÌÅ×ÀÍÈÅ

Обратите внимание на строку 4. Для лучшего восприятия прайса можно было бы объединить ячейки A4, B4, C4 и D4, задав для объединенной ячейки выравнивание текста по центру, однако в этом примере было просто скопировано форматирование ячейки B4 в C4 и D4. Как уже отмечалось, объединенние ячеек может вызвать проблемы связанные с совместимостью различных версий Excel, поэтому эта возможность не используется в рассматриваемом примере.

Как отделить внутреннюю информацию от внешней информации 29

Èñïîëüçîâàíèå ôîðìóë è àâòîçàïîëíåíèå ÿ÷ååê ôîðìóëàìè Итак, во “внутрифирменном” прайс-листе должны вводиться только значения базовой цены, а значения в столбцах “Опт” и “Цена” должны вычисляться автоматически. Нет ничего проще, так как рабочие листы Excel в частности и электронные таблицы вообще изначально создавались для решения подобных задач. Для этого надо всего лишь заполнить столбцы “Цена” и “Опт” соответствующими формулами. Для данного примера следует ввести в ячейку C5 формулу =D5*КУРС, а в ячейку B5 − =C5*РОЗНИЦА. После этого достаточно выделить прямоугольную область шириной в две ячейки так, чтобы ячейки с формулами (C5 и B5) представляли собой верхнюю строку этой области, и выполнить команду Ïðàâêà | Çàïîëíèòü | Âíèç. В результате вся область будет заполнена соответствующими формулами, а столбцы “Цена” и “Опт” будут отображать значения, вычисленные на основании данных столбца “База” (рис. 1.11).

Рис. 1.11. Столбцы "Цена" и "Опт" содержат формулы, а столбец "База" — числовые значения в денежном формате Теперь достаточно изменить значения курса или розничного коэффициента, и все цены будут автоматически вычислены заново. Это механизм нетрудно усложнить в соответствии с реальными потребностями фирмы — для введения дополнительных ступеней ценообразования необходимо лишь добавить новые столбцы с формулами.

Óñëîâíîå ôîðìàòèðîâàíèå ÿ÷ååê Чего еще нам не хватает для использования такого прайс-листа во “внутрифирменных” целях? Совершенно верно, надо знать, а есть ли вообще в данный момент, выбираемый товар на складе фирмы. А если есть, то, достаточно ли его, не пора ли позаботиться о поставке нового товара на склад из данной позиции? Все эти проблемы решаются введением дополнительного столбца, отражающего наличие товара. Пусть это будет столбец “Нал.”. Нет необходимости форматировать его для ввода денежных значений, но зато его следует отформатировать особенным образом. Здесь следует использовать условное форматирование, благодаря которому ячейки будут менять свой цвет в зависимости от своего содержимого. Пусть, например, при полном отсутствии товара по соответствующей позиции (значении 0) ячейка окрашивается красным, а при наличии менее 5 единиц — желтым. (Пора обратить внимание на эту позицию!)

Êàê ïðèìåíèòü ê ÿ÷åéêàì óñëîâíîå ôîðìàòèðîâàíèå 1. Выделите необходимые ячейки (в данном случае, ячейки под заголовком “Нал.”). 2. Выполните команду Формат | Условное форматирование. 3. В группе параметров Условие 1 диалогового окна Условное форматирование выберите из первых двух списков пункты значение и равно соответственно (рис. 1.12).

30 Глава 1. Excel: cоздание прайс-листа

Рис. 1.12. Все условия, заданные в диалоговом окне Условное форматирование, действуют независимо друг от друга 4. Введите в третье поле значение 0. 5. Щелкните на кнопке Формат и выберите на вкладке Вид, открывшегося диалогового окна красный цвет. Закройте диалоговое окно Формат ячеек щелчком мыши на кнопке ОК. 6. Вернувшись в диалоговое окно Условное форматирование, щелкните на кнопке А также. 7. Повторите всю последовательность в группе параметров Условие 2, выбрав пункты значение и больше, и введя в третье поле значение 5. В диалоговом окне Формат ячеек выберите желтый цвет. 8. Закройте диалоговое окно щелчком на кнопке OK. Прайс-лист стал достаточно удобным “инструментом” для использования внутри фирмы, но показывать клиенту его теперь нельзя (рис. 1.13).  см. также в гл. 8 раздел “Условные форматы”.

Ìåòîäû ðàçäåëåíèÿ èíôîðìàöèè íà “âíóòðåííþþ” è “âíåøíþþ” Пора приступить к разделению информации на “внутреннюю” и “внешнюю”. Как это сделать? Можно пойти по одному из многих путей, средства MS Office предоставляют разные методы для решения этой задачи. Пожалуй, здесь стоит остановиться на минутку и поразмыслить о последующих действиях. В среде Microsoft Office существуют специальные механизмы, позволяющие связывать самыми различными способами не только однородные, но даже и совершенно разного вида данные, относящиеся к разным приложениям и находящиеся в самых разных местах,… практически, где угодно.

Рис. 1.13. При полном отсутствии или наличии товара менее 5 единиц, ячейка в соответствующей позиции окрашивается в красный или желтый цвет

VBA — это очень просто! 31

Ñâÿçûâàíèå äàííûõ â Excel Связать два листа Excel в одной рабочей книге — смехотворно простая задача, если учитывать всю потенциальную мощь Office. Достаточно выделить в прайсе ту область, которую надо показать клиентам — столбцы A и B. Их можно выделить целиком или же в виде области под заголовками “Наименование” и “Цена”. Скопировать эту область в буфер обмена командой Ïðàâêà | Êîïèðîâàòü, а затем, открыв или создав новый лист, вставить и связать область при помощи команды Ïðàâêà | Ñïåöèàëüíàÿ âñòàâêà. При этом, открывшееся диалоговое окно Ñïåöèàëüíàÿ âñòàâêà следует закрыть щелчком мыши на кнопке Âñòàâèòü ñâÿçü (см. рис. 1.4). В результате данные из “внутрифирменного” листа будут автоматически отображаться в листе “клиентском”. Казалось бы, задача решена. Но устроит ли нас такое решение? Да, если на этом остановиться, и если дальнейшая автоматизация офиса больше не интересует. Нет, если достигнутого результата недостаточно, и надо усовершенствовать и облегчить работу по-настоящему радикальным образом.

Íåäîñòàòêè ñâÿçûâàíèÿ ëèñòîâ Excel В чем недостатки простого связывания клиентского листа с некоторой областью в листе “внутрифирменном”? • В первую очередь в том, что область отображается “как есть”, без какого-либо отбора. Но позиции, товар по которым на складе в данный момент отсутствует, не должны попадать в “клиентский” прайс-лист. • Кроме того, могут иметься и другие маркетинговые соображения, по которым отдельные позиции следует включать или не включать в список. Хорошее решение этой задачи должно давать возможность сотруднику отдела сбыта произвольно выбирать позиции товара для включения в прайс-лист. Иными словами, “клиентский” прайс-лист должен генерироваться по некоторым правилам из “внутрифирменного” листа. Все это приводит к мысли, что дальнейшее развитие невозможно с использованием только лишь простейших средств MS Office и настала пора приступить у программированию на VBA….

VBA — ýòî î÷åíü ïðîñòî! Многие люди, каждый день работающие в Word или Excel, считают, что VBA — это “что-то для программистов”. Напрасно они так думают. В рабочей среде MS Office есть множество сложных инструментов, которые, однако, обычные пользователи применяют, не задумываясь, и не пускаясь в рассуждения о том, насколько это сложно. Например, рядовой пользователь Word, который просто вводит текст и форматирует его по мере своего разумения, ужаснется, если узнает, насколько сложным может быть форматирование обычного текстового абзаца, и какие замысловатые механизмы можно в этом случае задействовать. Но вся эта потенциальная сложность не мешает ему просто ввести свой текст и отпечатать документ Word вполне пристойного вида, не постигая все “глубины” и тонкости, которые тут возможны. Точно так же дело обстоит и с VBA — программирование на VBA в некоторых случаях может быть очень сложной задачей. Но если сначала не очень усложнять задачу, и не увлекаться постижением “глубин”, то ничего особенно трудного в этом процессе нет. Итак, VBA — Visual Basic для приложений, вот тот инструмент, который потребуется для дальнейшей автоматизации созданного в предыдущих разделах прайс-листа. Для первого знакомства с этим интереснейшим инструментом выберем несложную задачу: генерация клиентского прайс-листа с пропуском некоторых, произвольно выбранных позиций. Для этого необходимо предоставить пользователю возможность каким-то образом отмечать строки списка, который лежит в основе прайса. ÏÐÈÌÅ×ÀÍÈÅ

Как можно сделать это вручную? С помощью диалогового окна Ôîðìàò ÿ÷ååê можно, как назначить для ячейки штриховку, так и отменить ее. Предполагается, что заштрихованное наименова-

32 Глава 1. Excel: cоздание прайс-листа ние товара служит хорошим обозначением позиции, которую следует исключить. Давайте создадим макрос на VBA, который будет выполнять эти действия автоматически, по двойному щелчку мыши на ячейке.

Ïåðâîå çíàêîìñòâî ñ Visual Basic Если для читателя это первая встреча с Visual Basic, то многое может показаться ему сложным и пугающим. Ничего страшного, это всего лишь первое впечатление, не стоит ему поддаваться.

Çàïóñê ðåäàêòîðà Visual Basic Итак, первый шаг заключается в том, чтобы открыть окно редактора Visual Basic. Для этого существует специальная команда в меню Ñåðâèñ, однако не будем пока ее использовать. Для того чтобы кратчайшим путем попасть именно в ту точку редактора Visual Basic, которая требуется в данном примере, нужно расположить указатель мыши на ярлыке с названием рабочего листа в нижней части окна Excel и щелкнуть правой кнопкой мыши. Текущим должен быть именно тот лист рабочей книги, для которого создается макрос. В результате откроется контекстное меню (рис. 1.14) данного рабочего листа, в котором нужно выполнить команду Èñõîäíûé òåêñò. У всех листов рабочей книги контекстное меню выглядит одинаково, однако команда Èñõîäíûé òåêñò в меню разных листов приводит к отображению разных окон в редакторе Visual Basic.

Рис. 1.14. Создание макроса при помощи контекстного меню рабочего листа Excel

Рис. 1.15. Окно кода рабочего листа, при открытии, автоматически создает заготовку текста стандартной процедуры

Ñîçäàíèå ïðîöåäóðû VBA В данном случае, на экране отобразится окно, показанное на рис. 1.15. Не стоит пугаться замысловатой структуры редактора Visual Basic — пока можно не обращать внимания на все остальное, кроме окна в центре экрана, служащего для ввода исходного текста процедур (программ VBA) выбранного рабочего листа. Автоматически в этом окне создается заготовка процедуры, содержащая ее заголовок и окончание. Но в этом случае нужен не этот макрос. В верхней части окна кода находятся два списка, определяющие конкретную процедуру (макрос). Разверните левый список и выберите в нем пункт Worksheet. Затем, в правом списке выберите пункт BeforeDoubleClick, как показано на рис. 1.16.

Первое знакомство с Visual Basic 33

1

2

Рис. 1.16. При помощи списков Object и Procedure можно создать заготовку стандартной процедуры В результате в окне кода будет создан заголовок нужной процедуры Worksheet_BeforeDoubleClick. Первая и последняя строки процедуры созданы автоматически, и теперь требуется ввести исходный текст процедуры (листинг 1.1). Или, как его еще называют, программный код. ËÈÑÒÈÍÃ 1.1

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Excel.Range, _ Cancel As Boolean) With ActiveCell If .Column = 1 Then If .Interior.Pattern = xlPatternNone Then .Interior.Pattern = xlPatternGray25 Else .Interior.Pattern = xlPatternNone End If Cancel = True End If End With End Sub На рис. 1.17 показано, как в результате должно выглядеть окно кода.

Рис. 1.17. Процедура Worksheet_BeforeDoubleClick

Ââîä èñõîäíîãî òåêñòà ïðîöåäóðû Редактор Visual Basic анализирует вводимый пользователем текст на предмет ошибок. По мере ввода какого-нибудь слова редактор предлагает ближайший подходящий вариант синтаксически правильного выражения языка. Например, если начать ввод слова Pattern, редактор, рядом с

34 Глава 1. Excel: cоздание прайс-листа курсором ввода, отобразит список, в котором будет найден и выделен ближайший подходящий вариант к уже введенной части слова. В этом случае можно продолжать вводить остальные буквы этого слова, не обращая внимания на поведение окна, или же ввести сразу символ, следующий за этим словом, например “=”, тогда остаток слова Pattern будет введен автоматически (рис. 1.18).

Рис. 1.18. Редактор Visual Basic предлагает свою помощь — рядом с курсором ввода появляется список имен, которые допустимы в данном выражении

Рис. 1.19. Возврат в окно Excel

В следующей строке программного кода вспомогательное окно появится дважды — перед словом Interior и после него. При опечатках или даже просто попытках перевести курсор ввода с неоконченной строки редактор может раскрыть окно с предупреждением об ошибке (рис. 1.19) — в этом случае надо щелкнуть на кнопке OK этого окна и исправить ошибку. Ошибочно набранное слово будет выделено красным цветом.

Рис. 1.20. Возврат в окно Excel

Рис. 1.21. Наименования товара, на которых выполнен двойной щелчок, заштрихованы

Наконец, исходный текст макроса введен, и можно испытать его в деле. Для возврата в окно Excel можно воспользоваться кнопкой на панели задач или специальной кнопкой со значком Excel, расположенной в левом верхнем углу окна редактора Visual Basic (рис. 1.20). Теперь попробуем выполнить двойной щелчок на одной из ячеек столбца “Наименование”.… Ура, работает! Ячейка заштрихована. Еще один двойной щелчок и штриховка исчезла. Теперь можем легко и просто отмечать позиции прайс-листа, которые не должны попасть в его версию для клиентов (рис. 1.21).

Âîçìîæíûå ïðîáëåìû è ìåòîäû èõ ðåøåíèÿ При одновременной работе в среде редактора Visual Basic и Excel иногда возникают ситуации, которые могут поставить в тупик неопытного пользователя. Ничего страшного. Для того чтобы усвоить принципы работы с макросами VBA в приложениях MS Office, требуется всего лишь немного практики. Ниже описаны некоторые нештатные ситуации, которые могут возникнуть при создании и отладке макросов.

Первое знакомство с Visual Basic 35 • •











Если окно редактора Visual Basic закрывает весь экран и панель задач не видна, щелкните на кнопке максимизации окна в правом верхнем углу (средняя из трех кнопок). Если при вводе исходного текста появляется окно с сообщением об ошибке “Ошибка компиляции. Ожидалось...”, щелкните на кнопке OK или нажмите клавишу [Esc], а затем исправьте ошибку. Если при переходе из окна Excel в окно Visual Basic нельзя ввести текст при помощи клавиатуры или выбрать, какой-нибудь элемент окна мышью, вернитесь в окно Excel и нажмите клавишу [Esc]. Видимо, Excel был оставлен в состоянии редактирования ячейки, а в такой ситуации окно Visual Basic не реагирует на действия пользователя. Если при выполнении макроса возникла ошибка, то на экране появится окно с сообщением “Ошибка выполнения...”. Если щелкнуть на кнопке Çàâåðøèòü, то выполнение макроса прервется — выполняемые им операции не будут завершены. В случае, когда макрос не может завершить свою работу из-за логической ошибки в тексте программы (при этом Excel может не реагировать на действия пользователя), необходимо нажать сочетание клавиш [Ctrl]+[Break] или, что, то же самое, [Ctrl]+[Pause]. Если выполнение программы прервано пользователем (см. выше), появится окно с сообщением “Выполнение программы прервано...”. В этом случае необходимо щелкнуть на кнопке Çàâåðøèòü. Если макрос “отказывается” стартовать, выдавая окно с сообщением “Невозможно выполнение программы в режиме прерывания”, следует перейти в окно редактора Visual Basic и выполнить команду Çàïóñê | Ñáðîñ (Run | Reset).

Àíàëèç ñòàíäàðòíîé ïðîöåäóðû îáðàáîòêè ñîáûòèÿ — äâîéíîãî ùåë÷êà ìûøüþ íà ÿ÷åéêå ëèñòà Excel Посмотрим теперь на текст процедуры VBA, которая была создана в предыдущих разделах (листинг 1.1). Пользователю, не имевшему ранее дела с VBA, на первый взгляд, код может показаться сплошной “китайской грамотой”, однако на практике все не так сложно, как может показаться. Давайте разберемся. В двух списках были выбраны пункты Worksheet и BeforeDoubleClick — в результате процедура (Sub) стала называться Worksheet_BeforeDoubleClick. Здесь Worksheet — это стандартный объект, для которого создавалась данная процедура — рабочий лист. BeforeDoubleClick — это стандартное событие рабочего листа, событие это происходит при каждом двойном щелчке на листе. Если бы в правом списке было выбрано другое событие, например, Activate, то код процедуры выполнялся бы при активации листа, то есть в момент, когда лист становится текущим. Но в нашем случае код будет выполняться при двойных щелчках мыши на любой ячейке данного листа. Что же, собственно, делает код процедуры? With ActiveCell … … End With Первая и последняя строчки определяют, что весь заключенный между ними код относится к объекту ActiveCell, то есть к текущей ячейке. Именно она-то нас и интересует. Поскольку любая ячейка, на которой выполнен двойной щелчок мыши, автоматически становится текущей. Оператор If .Column = 1 Then … … End If

36 Глава 1. Excel: cоздание прайс-листа означает “если−то” и ограничивает выполнение процедуры пределами столбца A. .Column — это свойство любой ячейки и указывает оно на номер столбца, к которому эта ячейка относится. Столбец “Наименование” расположен в рассматриваемой таблице первым по счету и если двойной щелчок выполнен на ячейке другого столбца, то .Column окажется не равным 1, и дальнейший код не будет выполнен.  см. также в приложении раздел “Операции сравнения”.

Следующий, самый внутренний оператор, выглядит сложнее. If–Then–Else означает “если−то−иначе” и в вольном переводе на обычный язык операторы If .Interior.Pattern = xlPatternNone Then .Interior.Pattern = xlPatternGray25 Else .Interior.Pattern = xlPatternNone End If означают: если ячейка не заштрихована, то задать ей узор 25%-й серый, в противном случае, то есть, если она уже заштрихована узором 25%-й серый, отменить штриховку ячейки.  см. в приложении раздел “Конструкция If … Then … Else … End If”.

И, наконец, маленькая строка Cancel = True играет здесь вспомогательную роль. Переменная Cancel является параметром процедуры, и смысл этого параметра таков: присвоив ему логическое значение True (Истина), пользователь как бы сообщается рабочему листу Excel, что двойной щелчок отменяется, т.е. его не было. Поэтому Excel не перейдет по двойному щелчку к редактированию ячейки, как он это обычно делает. ÏÐÈÌÅ×ÀÍÈÅ

Такой объект, как ячейка, обладает множеством свойств. Если, например, вместо .Interior.Pattern = xlPatternGray25 использовать оператор .Value = 100, то по двойному щелчку на ячейке в нее будет помещаться число 100. Можно использовать такой оператор, как .Interior.ColorIndex = 3 — в этом случае ячейка будет окрашиваться в красный цвет. Вернуть ей белый цвет можно оператором .Interior.ColorIndex = 2. Черному цвету соответствует значение 1, а серому — 15. Чтобы получить более подробную справку, достаточно выделить выражение ColorIndex и нажать клавишу [F1]. Можно использовать и другое событие, например, выбрав в списке Procedure выражение SelectionChange. Код, созданной процедуры менять не надо, должен измениться только ее заголовок. Это событие происходит каждый раз, когда текущей становится другая ячейка. В результате ячейки первого столбца можно будет выделять одиночным щелчком мыши, однако макрос будет выполняться и при простом перемещении по столбцу посредством клавиатуры. Какой вариант более удобен, но остается за разработчиком процедуры.

Åùå îäíî ñîáûòèå — åùå îäèí ìàëåíüêèé ìàêðîñ Чтобы закрепить достигнутого успеха в предыдущем разделе, выполним еще одно упражнение, которое, к тому же, позволит довести механизм выделения позиций в прайс-листе до совершенства. Пользователь может абсолютно произвольным образом решать, какие позиции необходимо включить в текущую версию “клиентского” прайс-листа, а какие — исключить. Но существует ряд позиций, по поводу которых нечего решать: позиции с закончившимся товаром все равно придется исключить. Пусть прайс выделяет такие позиции автоматически!

Первое знакомство с Visual Basic 37

Àâòîìàòè÷åñêîå âûäåëåíèå ïîçèöèé â ïðàéñå Прежде чем приступить к созданию макроса, подумаем, в какой момент времени и каким образом он должен выполняться? Воспользуемся уже имеющимся небольшим опытом в обработке событий рабочего листа. Макрос можно связать с элементом управления, например, командной кнопкой, размещенной на листе или с каким-нибудь клавиатурным эквивалентом, однако наилучшим будет такое решение, при котором пользователю не придется предпринимать специальные шаги для вызова макроса — когда процедура выполняется в ответ на какое-то событие Excel. Среди событий рабочего листа уже упоминалось событие Activate, которое генерируется в момент, когда лист становится активным (текущим). Это может произойти в момент открытия книги, если книга открывается на данном листе или же при переключении между листами в уже открытой книге. Если выбрать какой-то другой лист, а потом вернутся к листу с прайсом, то для него будет генерироваться событие Activate. Этот момент и будет наилучшим для выполнения макроса, который вычеркивает позиции прайс-листа с закончившимся товаром. В этом случае как бы ни открывался лист, он предстанет перед пользователем в уже подготовленном виде. Итак, необходимо создать макрос, который представляет собой процедуру обработки события Activate для рабочего листа. Для этого следует вновь вернуться в редактор Visual Basic, щелкнув правой кнопкой мыши на ярлыке с названием листа в нижней части окна, и выполнив в контекстном меню команду Èñõîäíûé òåêñò. В левом списке Object в верхней части окна кода выберите объект Worksheet (т.е. выбранный рабочий лист), а в правом Procedure — событие Activate. В результате будет создана заготовка процедуры Worksheet_Activate, в которую нужно ввести текст, приведенный на листинге 1.2. ËÈÑÒÈÍà 1.2

Private Sub Worksheet_Activate() Dim N As Integer For N = 5 To UsedRange.Rows.Count If Cells(N, 5).Value = 0 Then Cells(N, 1).Interior.Pattern = xlPatternGray25 End If Next N End Sub

Рис. 1.22. Процедура Worksheet_Activate в окне кода При вводе строки Dim N As, объявляющей переменную N, редактор Visual Basic предложит выбрать тип переменной из списка, раскрывшегося рядом с курсором ввода. Можно просто ввести слово Integer, не обращая внимания на происходящее на экране, а можно воспользоваться предложенной подсказкой. В итоге окно программы должно приобрести вид, как на рис. 1.22.  см. в приложении разделе “Переменные”.

Вернувшись в окно Excel, можно проверить действие макроса: для этого достаточно ввести нулевые значения в произвольные позиции столбца “Нал.”, а затем закрыть и вновь открыть книгу

38 Глава 1. Excel: cоздание прайс-листа или просто переключиться на другой лист, а затем вернуться к прайсу. Все обнуленные позиции буду заштрихованы.

Àíàëèç ñòàíäàðòíîé ïðîöåäóðû îáðàáîòêè ñîáûòèÿ — àêòèâèçàöèÿ ðàáî÷åãî ëèñòà Excel Взглянем на исходный текст последней процедуры. В результате выполнения строки Dim N As Integer будет создана переменная N типа Integer — она предназначена для хранения целочисленных значений.  см. в приложении подраздел “Integer” в разделе “Типы данных”.

Конструкцию For N = … To … … … Next N называют оператором цикла. Заключенный в нем код выполняется для всех значений переменной N, начиная с 5 и заканчивая числом…, которое возвращает выражение UsedRange.Rows.Count. Что это за число? Макрос должен просмотреть все строки прайслиста до самого конца, но ведь количество строк в конкретном прайсе заранее неизвестно! Выражение UsedRange.Rows.Count возвращает число использованных строк рабочего листа. Если самой нижней строкой, куда “доходил” пользователь, будет, скажем, 500-я строка, то выражение UsedRange.Rows.Count вернет значение 500.  см. в приложении раздел “Цикл For … Next”.

Наконец, условный оператор If Cells(N, 5).Value = 0 Then Cells(N, 1).Interior.Pattern = xlPatternGray25 End If выполняет следующую операцию: если в текущей строке (ее номер задает переменная N) ячейка 5го столбца содержит значение 0, то соответствующая ячейка первого столбца штрихуется узором “25%-й серый”. Вот и все, совершив эти действия поочередно для всех строк использованного диапазона, макрос тем самым выполнит свою задачу.  см. в приложении раздел “Конструкция If … Then … Else … End If”.

Ãåíåðàöèÿ êëèåíòñêîãî ïðàéñ-ëèñòà Настала пора перейти к решению главной задачи. Как сгенерировать текущую клиентскую версию прайс-листа на основе внутрифирменного прайса. Программный код, способный выполнить эту операцию, будет несколько сложнее, чем те процедуры, с которые до сих пор былм рассмотрены. По этой причине эта задача будет решена в несколько этапов: вначале сделаем это в упрощенном варианте, немного этот вариант усовершенствуем и только затем перейдем к полноценному решению. Прежде всего, необходимо решить вопрос запуска макроса. В данном случае нет смысла привязать его к какому-либо событию рабочего листа, поскольку пользователь может принять решение о генерации в произвольный момент времени. Поэтому лучше использовать элемент управления, при помощи которого можно было бы инициировать выполнение макроса. Самый простой способ снабдить лист Excel каким-нибудь элементом управления заключается в использовании

Генерация клиентского прайс-листа 39 панели инструментов Ôîðìû (рис. 1.23). Чтобы отобразить на экране эту панель, необходимо выполнить команду Âèä | Ïàíåëè èíñòðóìåíòîâ | Ôîðìû.

Рис. 1.23. Панель инструментов Ôîðìû содержит различные элементы управления, которые можно поместить на рабочий лист Excel Элемент управления Êíîïêà наилучшим образом подходит для запуска макроса. Командная кнопка создана для того, чтобы подавать команду, она-то нам и нужна. Необходимо поместить на лист одну кнопку, снабдить ее надписью, скажем, “Копия” и связать с пустым макросомзаготовкой.

Êàê ïîìåñòèòü íà ëèñò ýëåìåíò óïðàâëåíèÿ Если панель инструментов Формы не видна, выполните команду Вид | Панели инструментов | Формы. 2. Выберите щелчком мыши на панели Формы элемент управления Кнопка. 3. Поместите указатель мыши на лист Excel, в то место, где необходимо создать кнопку. Указатель мыши примет форму креста. Нажав левую кнопку мыши, растяните на экране, появившийся прямоугольник до необходимых размеров (рис. 1.24а). Отпустите кнопку мыши для завершения создания кнопки.

1.

а

б Рис. 1.24. Процесс создания кнопки на рабочем листе Excel 4. 5.

В указанном месте будет создана командная кнопка с надписью “Кнопка1” (рис. 1.24б). На экране появится диалоговое окно Назначить макрос объекту (рис. 1.25).

Рис. 1.25. Диалоговое окно Назначить макрос объекту 6.

Закройте диалоговое окно щелчком на кнопке Создать. В результате откроется окно Visual Basic с заготовкой макроса.

40 Глава 1. Excel: cоздание прайс-листа Вернитесь в окно. Вновь созданная кнопка будет по-прежнему окружена двойной штриховкой, например, выполнив команду View | Microsoft Excel ([Alt]+[F11]) редактора Visual Basic. 8. Щелкните на кнопке один раз левой кнопкой мыши, при этом штриховка изменится на одинарную. 9. Измените надпись на кнопке с “Кнопка1” на “Копия” (рис. 1.26) или другую надпись на свое усмотрение. При необходимости отформатируйте надпись, используя инструменты панели Форматирование Microsoft Excel.

7.

Рис. 1.26. Изменение свойств кнопки на рабочем листе Excel 10. Поместите указатель в любое место листа за пределами кнопки и щелкните левой кнопкой или же просто нажмите клавишу [Esc]. Штриховка исчезнет, и кнопка будет готова к работе.

Îñîáåííîñòè èçìåíåíèÿ ñâîéñòâ ýëåìåíòîâ óïðàâëÿþùèõ ðàáî÷èì ëèñòîì Пока элемент управления (в данном примере — кнопка) окружен двойной штриховкой, его можно переместить в другое место или изменить его размеры. При обычной штриховке элемент управления (кнопка) позволяет изменить надпись на нем. Необходимо, отличать надпись на объекте от его имени. Замена надписи никак не влияет на объект. Созданная в предыдущем разделе кнопка по-прежнему называется “Кнопка1”. Если вокруг объекта нет никакой штриховки, кнопка готова к работе. Указатель мыши над ней приобретает вид руки с вытянутым указательным пальцем, а щелчок левой кнопкой мыши приведет командную кнопку в действие — она выполнит макрос с именем Кнопка1_Щелкнуть. Пока что процедура, соответствующая этому макросу пуста, и состоит только из заголовка и окончания. Поэтому ее выполнение не приведет ни к какому действию.

Рис. 1.27. Диалоговое окно Назначить макрос объекту позволяет связать выбранный элемент управления с любым из доступных макросов, а также открыть окно кода с уже назначенным элементу управления макросом

Ïðîñòåéøèé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà Теперь можем заполнить заготовку процедуры программным кодом VBA. Для этого необходимо щелкнуть правой кнопкой мыши на созданной кнопке “Копия” и выбрать в контекстном меню команду Íàçíà÷èòü ìàêðîñ. В отобразившемся диалоговом окне Íàçíà÷èòü ìàêðîñ îáúåêòó (рис. 1.27) необходимо выделить название требуемого макроса (в данном случае “Кноп-

Генерация клиентского прайс-листа 41 ка1_Щелкнуть”), и щелкнуть на кнопке Ïðàâêà. На экране раскроется окно кода с заготовкой макроса. Программный код (исходный текст) макросов, связанных с элементами управления, располагается в программных модулях, и, в отличие от знакомых уже по предыдущими разделам процедурам обработки событий, не связан с объектами рабочей книги. Программный код будет выполняться по щелчку на созданной кнопке с надписью “Копия”. Вначале создадим максимально упрощенный вариант макроса (листинг 1.3). ËÈÑÒÈÍÃ 1.3

Sub Кнопка1_Щелкнуть() Dim NewWS, OldWS As Worksheet Set OldWS = ActiveSheet Set NewWS = Worksheets.Add OldWS.UsedRange.Copy (NewWS.Range("A1")) End Sub

Àíàëèç ïðîöåäóðû ãåíåðàöèè ïðàéñà Этот простейший макрос выполняет несколько элементарных операций, которые без труда можно выполнить, используя команды меню Excel. Он вставляет в книгу новый лист и копирует туда содержимое листа с прайсом. Выражение Dim объявляет две переменные типа Worksheet — такие переменные предназначены для хранения целого рабочего листа Excel. Переменной OldWS присваивается в качестве значения объект ActiveSheet, то есть текущий рабочий лист с внутрифирменным прайсом. А переменная NewWS становится новым рабочим листом при помощи операции Worksheets.Add, которая, выражаясь языком профессиональных программистов, называется вызовом метода Add, принадлежащего семейству Worksheets. Этот метод можно рассматривать, как аналогию команды Ëèñò из меню Âñòàâêà. Переменная NewWS становится новым рабочим листом, который вставляется в книгу Excel. Новый лист получает имя по умолчанию. Строка OldWS.UsedRange.Copy (NewWS.Range("A1")) в переводе на обычный язык означает: скопировать использованный диапазон объекта OldWS (то есть область прайса из старого листа) в буфер обмена и вставить его из буфера в позицию A1 нового листа. Метод Copy аналогичен действию команды Êîïèðîâàòü меню Ïðàâêà. А благодаря тому, что сразу после обращения к этому методу указана цель копирования, то одновременно выполняется и метод вставки аналогичный действию команды Âñòàâèòü меню Ïðàâêà.

Рис. 1.28. При попытке вставить область, содержащую в себе именованные ячейки, предупреждение Excel появится столько раз, сколько таких объектов имеется во вставляемой области В любом случае, при выполнении копирования и вставки именованных ячеек, которые содержит прайс, Excel выдаст предупреждение, показанное на рис. 1.28. Действительно, ведь копируется все содержимое всей использованной области листа, и в том числе именованные ячейки “КУРС” и “РОЗНИЦА”, а также ячейки с формулами, где эти имена

42 Глава 1. Excel: cоздание прайс-листа используются. Если два раза щелкнуть на кнопке Äà, то вставка будет завершена успешно (см. рис. 1.29).

Рис. 1.29. Если позиция и имя нового листа не указаны, то он вставляется перед текущим листом и получает имя по умолчанию. Область прайс-листа вставлена “как есть“ Новый прайс выглядит почти так же, как и исходный лист: содержимое ячеек и их форматирование, скопирована даже командная кнопка “Копия“. Кроме ширины ячеек, которая стала стандартной. Но это легко исправить. Но, если изучить содержимое ячеек, то можно обнаружить, что в столбцах B и C содержатся все те же формулы, но нет именованных ячеек “КУРС” и “РОЗНИЦА”.

Íåäîñòàòêè ïðîñòåéøå ïðîöåäóðû ãåíåðàöèè ïðàéñà Конечно, в столь примитивном макросе нет никакого смысла. Во-первых, незачем создавать макрос, выполняющий то, что без труда делается вручную. Во-вторых, целью этого примера является отбор информации для клиента, в то время как созданный макрос не выполняет никакой выборки данных.

• •

Óñîâåðøåíñòâîâàíèÿ ïðîöåäóðû ãåíåðàöèè ïðàéñà Новую процедуру нетрудно усовершенствовать.

Îãðàíè÷åíèå îáëàñòè êîïèðîâàíèÿ ÿ÷ååê Прежде всего, ограничим область копирования только прямоугольником 2х43 (предположим, тренировочный прайс занимает 43 строки). Для этого при вызове метода Copy укажем точный диапазон, а саму операцию копирования разобьем на два этапа: копирование и вставка. Таким образом, заменим строку OldWS.UsedRange.Copy (NewWS.Range("A1")) на: OldWS.Range("A1", "B43").Copy NewWS.Range("A1").PasteSpecial Метод PasteSpecial соответствует команде Ñïåöèàëüíàÿ âñòàâêà из меню Ïðàâêà.

Генерация клиентского прайс-листа 43

Èçìåíåíèå øèðèíû ÿ÷ååê è ñòîëáöîâ Далее, чтобы изменить ширину ячеек нового листа, обратимся к свойству ColumnWidth, которое отвечает за ширину ячейки, а значит, и ширину столбца, поскольку все ячейки в столбце имеют одинаковую ширину. Например, ширину ячейки А1 можно получить при помощи ее свойства ColumnWidth, а присваивание значения этому свойству изменит ширину всего столбца А. Обратиться к ячейке А1 можно при помощи выражения Cells(1, 1) — свойство рабочего листа. Таким образом, в данном случае, ширина первого столбца в новом рабочем листе определяется выражением NewWS.Cells(1, 1).ColumnWidth Нетрудно догадаться, что задать первым двум столбцам нового рабочего листа такую же ширину, какую имеют столбцы старого, можно посредством следующих операторов: NewWS.Cells(1, 1).ColumnWidth = OldWS.Cells(1, 1).ColumnWidth NewWS.Cells(1, 2).ColumnWidth = OldWS.Cells(1, 2).ColumnWidth

Èçìåíåíèå èìåíè ðàáî÷åãî ëèñòà Excel Наконец, еще одно небольшое усовершенствование — пусть новый лист сразу получает имя, соответствующее его назначению: NewWS.Name = "Расходные материалы"  см. также в этой главе раздел “Исключение повторения имен листов рабочей книги Excel”.

Òåñòèðîâàíèå ðàáîòû íîâîé ïðîöåäóðû ãåíåðàöèè ïðàéñà В результате процедура приобретет вид, как на листинге 1.4. ËÈÑÒÈÍÃ 1.4

Sub Кнопка1_Щелкнуть() Dim NewWS, OldWS As Worksheet Set OldWS = ActiveSheet Set NewWS = Worksheets.Add NewWS.Name = "Расходные материалы" NewWS.Cells(1, 1).ColumnWidth = OldWS.Cells(1, 1).ColumnWidth NewWS.Cells(1, 2).ColumnWidth = OldWS.Cells(1, 2).ColumnWidth OldWS.Range("A1", "B43").Copy NewWS.Range("A1").PasteSpecial End Sub Введите этот программный код в подготовленную процедуру Кнопка1_Щелкнуть в окне редактора Visual Basic и, вернувшись в Excel, запустите процедуру при помощи кнопки “Копия”. Получится что-то похожее на рис. 1.30.

44 Глава 1. Excel: cоздание прайс-листа

Рис. 1.30. Столбец B по-прежнему содержит формулы Странно, почему-то все значения в столбце “Цена“ стали равными нулю. Впрочем, ничего странного, ведь столбец по-прежнему содержит формулы, которые ссылаются на значения в столбце C, а он теперь пуст! Если бы выполнить копирование при помощи команды Ïðàâêà | Ñïåöèàëüíàÿ âñòàâêà, то можно было бы получить аналогичный результат.

Êîïèðîâàíèå ðåçóëüòàòîâ âû÷èñëåíèé ïî ôîðìóëàì è èõ ôîðìàòèðîâàíèÿ Чтобы скопировать значения, то есть результаты вычислений по формулам вместо самих формул, в группе параметров Âñòàâèòü диалогового окна Ñïåöèàëüíàÿ âñòàâêà (см. рис. 1.4) необходимо выбрать переключатель çíà÷åíèÿ. При этом форматирование ячеек не будет скопировано, поэтому команду Ñïåöèàëüíàÿ âñòàâêà придется выполнить еще раз, теперь уже с переключателем в положении форматы. В результате клиентский прайс-лист будет “отрезан” от формул в исходной таблице, но сохранит текущие числовые значения. В коде VBA, при обращении к методу PasteSpecial, переключателю Âñòàâèòü соответствует параметр Paste. Если необходимо вставить только значения, то надо добавить оператор Paste:=xlPasteValues. Для вставки форматов параметру необходимо присвоить значение xlPasteFormats. В результате операция копирование и вставка преобразуется следующим образом: OldWS.Range("A1", "B43").Copy NewWS.Range("A1").PasteSpecial Paste:=xlPasteValues NewWS.Range("A1").PasteSpecial Paste:=xlPasteFormats

Èñêëþ÷åíèå ïîâòîðåíèÿ èìåí ëèñòîâ ðàáî÷åé êíèãè Excel Кроме того, необходимо решить еще одну маленькую проблему: в рабочей книге Excel не может быть двух листов с одинаковыми именами, поэтому повторный вызов макроса приведет к ошибке. Чтобы при каждом вызове макроса имя вставляемого листа изменялось, включим в состав имени количество листов в рабочей книге. Оно будет увеличиваться с каждым новым листом. Для этого достаточно добавить в строку для присваивания имя новому листу выражение Str(Worksheets.Count), которое на языке программистов означает “число объектов в семействе Worksheets, преобразованное в строковое значение”. Итак, макрос в новой редакции будет выглядеть, как на листинге 1.5. ËÈÑÒÈÍà 1.5

Sub Кнопка1_Щелкнуть()

Генерация клиентского прайс-листа 45 Dim NewWS, OldWS As Worksheet Set OldWS = ActiveSheet Set NewWS = Worksheets.Add NewWS.Name = "Расходные материалы" + Str(Worksheets.Count) NewWS.Cells(1, 1).ColumnWidth = OldWS.Cells(1, 1).ColumnWidth NewWS.Cells(1, 2).ColumnWidth = OldWS.Cells(1, 2).ColumnWidth OldWS.Range("A1", "B43").Copy NewWS.Range("A1").PasteSpecial Paste:=xlPasteValues NewWS.Range("A1").PasteSpecial Paste:=xlPasteFormats End Sub Теперь, после выполнения нового макроса, в столбце B клиентского прайса останутся результаты вычислений по формулам, но без самих формул.

Ïîëíîöåííûé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà Однако, даже усовершенствованный макрос все еще не делает того, ради чего его разрабатывали — не пропускает заштрихованные позиции при копировании их в клиентский прайс-лист. Но теперь все готово, чтобы “его этому научить”. Необходимо, чтобы макрос применял операцию копирования не ко всей области прайс-листа сразу, а к каждой строке поочередно, анализируя при этом состояние ячейки в столбце A. Для этого потребуются две переменные, способные хранить целочисленные значения. Переменная L будет использована для последовательного перебора номеров всех строк в исходной таблице, а в переменной M будет подсчитываться число строк нового листа — заштрихованные строки будут пропускаться. Для этого используем оператор цикла, который уже был рассмотрен в этой главе. ← см. в этой гл. раздел “Автоматическое выделение позиций в прайсе” (Листинг 1.2).

M = 3 For L = 3 To 43 ... M = M + 1 Next L Переменная L будет автоматически принимать значения от 3 до 43 (от 3, потому что первые две строки пока будут пропущены), а переменная M будет… принимать значения от 3 до 43, если ни одна из строк не заштрихована. Если исходная таблица содержит заштрихованные ячейки, их надо пропустить. Для этого внутрь цикла нужно вставить условный оператор, который должен выявлять заштрихованные ячейки в исходном списке. В случае, если ячейка столбца A не заштрихована, выполняется увеличение значения переменной M и копирование строки в новый прайс. Если ячейка столбца A заштрихована, тогда значение переменной M должно остаться неизменным.  см. также в приложении раздел “Арифметические операции”.

Как проверить, заштрихована ли ячейка, уже было рассмотрено, поэтому сразу можно набросать эскиз цикла:

46 Глава 1. Excel: cоздание прайс-листа M = 3 For L = 3 To 43 If OldWS.Cells(L, 1).Interior.Pattern xlPatternGray25 Then ... M = M + 1 End If Next L ← см. в этой гл. раздел “Анализ стандартной процедуры обработки события — двойного щелчка мышью на ячейке листа Excel”.

Осталось придумать, как осуществлять копирование строк — ведь в нашем случае копировать строки целиком нельзя. С помощью еще одной переменной, назовем ее N, можем копировать по две ячейки L-строки исходного листа в M-строку нового листа: For N = 1 To 2 OldWS.Cells(L, N).Copy NewWS.Cells(M, N).PasteSpecial Paste:=xlPasteValues NewWS.Cells(M, N).PasteSpecial Paste:=xlPasteFormats Next N Теперь можем написать макрос, который будет при копировании строк пропускать заштрихованные позиции (листинг 1.6). ËÈÑÒÈÍÃ 1.6

Sub Кнопка1_Щелкнуть() Dim NewWS, OldWS As Worksheet Dim L, M, N As Integer Set OldWS = ActiveSheet Set NewWS = Worksheets.Add NewWS.Name = "Расходные материалы" + Str(Worksheets.Count) NewWS.Cells(1, 1).ColumnWidth = OldWS.Cells(1, 1).ColumnWidth NewWS.Cells(1, 2).ColumnWidth = OldWS.Cells(1, 2).ColumnWidth M = 3 For L = 3 To 43 If OldWS.Cells(L, 1).Interior.Pattern xlPatternGray25 Then For N = 1 To 2 OldWS.Cells(L, N).Copy NewWS.Cells(M, N).PasteSpecial _ Paste:=xlPasteValues NewWS.Cells(M, N).PasteSpecial _ Paste:=xlPasteFormats Next N M = M + 1

Генерация клиентского прайс-листа 47 End If Next L End Sub

Íåäîñòàòêè íîâîãî âàðèàíòà ïðîöåäóðû ãåíåðàöèè ïðàéñà Наконец-то! Теперь макрос делает то, ради чего он создан. Но о совершенства ему еще далеко. Во-первых, он вынужден пропускать две строки в заголовке таблицы, поскольку операции с объединенными ячейками должны применяться к объединенной области целиком. Адресация части объединенной ячейки возможна, но потребует некоторого усложнения кода. Кроме того, в этом вопросе проявляются различия между версиями Excel. Следовательно, скопировать диапазон A1:B2 необходимо целиком. • Во-вторых, во внутрифирменном прайс-листе был использован механизм группирования строк, и этот факт должен как-то отразиться в копируемых данных. • Наконец, фиксированную нижнюю границу в 43 строки необходимо заменить выражением OldWS.UsedRange.Rows.Count, которое возвращает нижнюю границу использованного диапазона на исходном листе, поскольку макрос претендует теперь на универсальность. •

Îêîí÷àòåëüíûé âàðèàíò ïðîöåäóðû ãåíåðàöèè ïðàéñà Копирование диапазона A1:B2 не представляет никакой трудности, здесь нужно применить тот же самый метод Copy ко всему диапазону, а для группирования данных в новом листе воспользуемся свойством OutlineLevel, в котором содержится уровень группировки той строки, о свойстве (OutlineLevel) которой идет речь. Достаточно перенести значения из свойства строк OutlineLevel исходной таблицы и новая таблица будет сгруппирована аналогичным образом. Таким образом, в своем окончательном варианте “макрос выборочного копирования строк” приобретает вид, как на листинге 1.7. ËÈÑÒÈÍà 1.7

Sub Кнопка1_Щелкнуть() Dim NewWS, OldWS As Worksheet Dim L, M, N As Integer Set OldWS = ActiveSheet Set NewWS = Worksheets.Add NewWS.Name = "Расходные материалы" + Str(Worksheets.Count) NewWS.Cells(1, 1).ColumnWidth = OldWS.Cells(1, 1).ColumnWidth NewWS.Cells(1, 2).ColumnWidth = OldWS.Cells(1, 2).ColumnWidth M = 3 For L = 3 To OldWS.UsedRange.Rows.Count If OldWS.Cells(L, 1).Interior.Pattern xlPatternGray25 Then For N = 1 To 2 OldWS.Cells(L, N).Copy NewWS.Cells(M, N).PasteSpecial Paste:=xlPasteValues NewWS.Cells(M, N).PasteSpecial Paste:=xlPasteFormats NewWS.Rows(M).OutlineLevel = OldWS.Rows(L).OutlineLevel Next N M = M + 1 End If

48 Глава 1. Excel: cоздание прайс-листа Next L OldWS.Range("A1", "B2").Copy NewWS.Range("A1", "B2").PasteSpecial End Sub Что ж, задача решена. Готовый клиентский прайс-лист (рис. 1.31) нетрудно теперь опубликовать в виде Web-документа, отпечатать или передать потенциальным клиентам в виде файла на дискете или вложения в сообщение электронной почты.

Рис. 1.31. Теперь скопированы также заголовок и уровни группировки строк

Глава 2 Excel: ïðàéñ-ëèñò äëÿ àâòîìàòè÷åñêîãî ñîñòàâëåíèÿ çàêàçà Ïîñòàíîâêà çàäà÷è Задача автоматизации прайс-листа, как и любая задача автоматизации многомерна. Достаточно посмотреть на столбцы с товарами и ценами с другой стороны, как бы “примерить” к ним другие практические операции, взятые из повседневной офисной практики, и на ум приходят совершенно другие подходы в использовании прайс-листа. Назначение прайс-листа, в конечном счете, состоит даже не в том, чтобы его прочитал потенциальный клиент. Конечная цель прайс-листа — обеспечение продаж. Клиент прочитал прайслист, нашел приемлемые для себя позиции, и (внимание, всему персоналу фирмы — не дышать!) хочет сделать заказ. “Выпишите счет, пожалуйста”. И тут начинается……. Фиксация заказа клиента “на бумаге” и выписка счета может быть вполне тривиальной задачей, если речь идет об одиночных товарных позициях, но нередки случаи, когда это оказывается непростым делом. Распространенный случай подобного рода задачи — комплектация готового сборного изделия, где каждая часть может быть выбрана из некоторого списка вариантов, но не все варианты совместимы между собой, при этом любое изменение набора влияет на конечную цену изделия. Хороший пример такого товара — персональный компьютер, который состоит из набора взаимосвязанных частей, каждая из которых существует и предлагается на рынке во многих вариантах. Очень часто, конфигурацию конкретного компьютера клиент заказывает сам, выбирая монитор, тип процессора, емкость и модель жесткого диска, а также многое другое. Выбирает он все это, естественно, из соответствующих разделов прайс-листа. Компьютер можно укомплектовать одним из десятков процессоров, но не всякий процессор подойдет к конкретной материнской плате. Каждое сочетание тут обладает своими особенностями. А какой видеоадаптер выбрать? Ой, что-то дороговато получается, может, сэкономить на ОЗУ или выбрать жесткий диск подешевле? А вот на мониторе не экономят — зрение дороже, но возникает вопрос, достаточен ли этот видеоадаптер для того, чтобы великолепные характеристики монитора не пропадали напрасно? Вопросы и вопросы, варианты и варианты… За каждым из таких вариантов стоит конкретная цена, и клиенту было бы очень удобно, если бы он мог с легкостью выбирать варианты, как вздумается, все время видеть “вытекающую из варианта” цену компьютера и при этом не попадать на запретную территорию — те сочетания компонентов, которые технически неработоспособны. А раз уж заказ сформирован, то неплохо было бы тут же отпечатать счет…. Что ж, это хорошая задача. Она хороша, во-первых, тем, что конкретно полезна — выбор конфигурации компьютера стал в наше время едва ли не массовым занятием. Во-вторых, принцип решения этой задачи легко распространить на многие сходные случаи, при этом решение не придется усложнять. Наконец, она хороша тем, что легко и естественно вписывается в практически применяемые способы работы — ведь прайсы в форме рабочих листов Excel существуют и используются повсеместно. В том числе речь идет и о прайс-листах фирм, продающих компьютеры с конфигурацией “на заказ”. Таким образом, объект автоматизации уже существует, остается лишь применить к нему инструментальные средства MS Office….

Выбор наименования из списка 51

Âûáîð íàèìåíîâàíèÿ èç ñïèñêà Итак, познакомимся с еще одним прайс-листом ООО “Универсал” — “PC-комплектующие” (рис. 2.1). Комплектующие персональных компьютеров здесь разбиты на разделы точно таким же образом, как это было сделано в прайс-листе “Расходные материалы” (см. предыдущую главу). Точно так же столбец A здесь содержит наименования товаров, а столбец B — соответствующие этим наименованиям цены. Это клиентский прайс-лист, который мог быть сгенерирован из внутрифирменного прайса таким же точно образом, как это было сделано в предыдущей главе.

Рис. 2.1. Прайс-лист “PC-комплектующие“ Таким образом, исходная ситуация ничем не отличается от прайс-листа “Расходные материалы”. Но автоматизация прайса примет теперь другое направление. Вначале, для того чтобы создать основу для автоматизации на уровне макросов VBA, потребуется выполнить несколько подготовительных операций.

Èìåíîâàííûå äèàïàçîíû В предыдущей главе ячейкам рабочего листа уже присваивались имена, но сейчас необходимо присвоить имя целому диапазону ячеек. Зачем в этом случае нужен именованный диапазон? Смысл имени заключается не только в том, что на него легче ссылаться, а назначение названного им диапазона становится понятным без дополнительных объяснений. Дело еще и в том, что при работе с диапазоном ячеек всегда может потребоваться удалить ставшие ненужными строки или же наоборот, вставить новые. Если при создании макроса привязаться к границам диапазона при помощи ссылок на ячейки (например, A5, B7), то при вставке или удалении строк (столбцов) границы диапазона изменятся, и ссылки станут неправильными. Если же присвоить диапазону A5:B7 имя, а затем, например, вставим после 5-й строки еще одну строку, то все эти строки, включая строку 8 (бывшую 7), будут относиться к тому же именованному диапазону. Такой механизм позволяет, как угодно менять размеры какого-то раздела в прайс-листе, ничего не меняя в коде VBA, который этот прайс обрабатывает.

52 Глава 2. Excel: прайс-лист для автоматического составления заказа

Рис. 2.2. Создание именованного диапазона

Рис. 2.3. Диапазону A5:B7 присваивается имя ПАМЯТЬ

Итак, для начала присвоим имя ПАМЯТЬ диапазону ячеек A5:B7, который в прайсе представляет раздел оперативной памяти для компьютеров (область под заголовком “ПАМЯТЬ”).

Êàê ñîçäàòü èìåíîâàííûé äèàïàçîí Для присвоения имени диапазону выполните следующие действия. Выделите требуемый диапазон ячеек (в данном случае это диапазон A5:B7), как показано на рис. 2.2. 3. Выполните команду Âñòàâêà | Èìÿ | Ïðèñâîèòü. На экране появится диалоговое окно Ïðèñâîåíèå èìåíè (рис. 2.3). 4. В поле Èìÿ введите, например, строку “ПАМЯТЬ”. 5. Закройте диалоговое окно щелчком мыши на кнопке OK.

1. 2.

 см. также в гл. 4 раздел “Подключение анализируемых прайс-листов”, где описан еще один метод создания именованного диапазона.

Диапазону ячеек A5:B7 будет присвоено имя ПАМЯТЬ. Теперь можно сослаться на ячейку A5, как на объект, например, Range(“ПАМЯТЬ”).Cells(1,1) Эта запись обозначает первую ячейка в первой строке именованного диапазона ПАМЯТЬ. Казалось бы, какая разница? Ведь с таким же успехом можно обратиться к этой ячейке напрямую: Range(“A5”) или Cells(5,1) Но разница все же есть. При работе с прайс-листом любой раздел может менять свои размеры при вставке или удалении строк в нем самом или сдвигаться при вставке или удалении в предыдущих разделах. Благодаря присвоенному имени не нужно обо всем этом беспокоиться — Range(“ПАМЯТЬ”).Cells(1,1) всегда будет указывать на первую ячейку данного раздела. Действуя аналогичным образом, далее следует присвоить имена всем остальным разделам прайс-листа. Вот список разделов в учебно-тренировочном прайсе рассматриваемого примера, а в скобках приведены имена, которые можно присвоить соответствующим диапазонам: ПАМЯТЬ (ПАМЯТЬ) МАТЕРИНСКИЕ ПЛАТЫ (МАТЕРИНСКИЕ_ПЛАТЫ) ПРОЦЕССОРЫ (ПРОЦЕССОРЫ)

Построение бланка заказа 53 ЖЕСТКИЕ ДИСКИ (ЖЕСТКИЕ_ДИСКИ) ВИДЕОКАРТЫ (ВИДЕОКАРТЫ) ЗВУКОВЫЕ КАРТЫ (ЗВУКОВЫЕ_КАРТЫ) КОРПУСА (КОРПУСА) МОНИТОРЫ (МОНИТОРЫ) МЫШИ, КЛАВИАТУРЫ (МЫШИ_КЛАВИАТУРЫ) ÏÐÈÌÅ×ÀÍÈÅ

Имена диапазонов не должны включать в себя пробелы или знаки пунктуации, поэтому соответствующие символы были заменены знаком подчеркивания. В результате все указанные имена должны присутствовать в окне Ïðèñâîåíèå èìåíè (рис. 2.4).

Рис. 2.4. Весь прайс-лист “покрыт“ именованными диапазонами

Ïîñòðîåíèå áëàíêà çàêàçà Далее нам необходимо создать бланк заказа, который основывался бы на прайс-листе “PCкомплектующие”, и позволял изменять конфигурацию компьютера, исходя из имеющихся в прайслисте компонентов. Лучше всего будет делать это на отдельном рабочем листе. Разумеется, речь должна идти о листе в той же самой рабочей книге. Создадим вначале заголовок бланка — он может быть абсолютно произвольным. Допустим, в первых двух строках поместим наименование бланка и его подзаголовки с тем, чтобы начать построение собственно бланка с ячейки A3, как изображено на рис. 2.5. Каждая строка заказа должна будет иметь довольно сложное устройство, поэтому наилучший способ построения заключается в том, чтобы полностью сформировать одну законченную и действующую строку, а затем размножить ее путем копирования и вставки. Скопированные строки все равно затем придется немного подкорректировать, но такой подход все же сэкономит много времени. ← см. в гл. 1 раздел “Как форматировать раздел при помощи команды Специальная вставка”.

Рис. 2.5. Построение бланка заказа 1.

Итак, создадим первую строку. Пусть она будет соответствовать монитору, которым должен быть укомплектован персональный компьютер. Для этого нам потребуется ввести в

54 Глава 2. Excel: прайс-лист для автоматического составления заказа некоторые ячейки формулы и поместить в одну из ячеек элемент управления “Поле со списком”. Еще его называют комбинированным полем. 2. Вначале введем в ячейку A3 строку “Монитор” — это просто обозначение компонента, за который отвечает данная строка. 3. Далее необходимо ввести в ячейку B3 формулу =ИНДЕКС(МОНИТОРЫ; D3; 1), как это показано на рис. 2.6 — смысл этой формулы станет ясен немного дальше.

Рис. 2.6. Ввод формулы в ячейку B3 4.

В ячейку C3 следует ввести похожую формулу: =ИНДЕКС(МОНИТОРЫ; D3; 2). При этом ячейка B3 отобразит ошибку формулы (#ЗНАЧ!), не обращайте на это внимания (рис. 2.7).

Рис. 2.7. Ввод формулы в ячейку C3 5.

Следующий шаг состоит в том, чтобы поместить в ячейку B3 элемент управления “Поле со списком” и настроить его свойства так, чтобы он правильно взаимодействовал с введенными формулами.

Êàê ñîçäàòü è íàñòðîèòü ýëåìåíò óïðàâëåíèÿ Ïîëå ñî ñïèñêîì 1.

Если панель инструментов Формы не видна, выполните команду Вид | Панели инструментов | Формы.

Рис. 2.8. Панель Формы 2. 3.

Выберите щелчком мыши на панели Формы элемент управления Поле со списком (рис.2.8). “Нарисуйте” указателем мыши местоположение поля в ячейке B3, как показано на рис.2.9.

Рис. 2.9. Создание Поле со списком

Построение бланка заказа 55 4. 5. 6. 7.

8.

Щелкните на поле со списком правой кнопкой мыши и выберите в контекстном меню команду Формат объекта. В диалоговому окне Форматирование объекта выберите вкладку Элемент управления и введите в поле Формировать список по диапазону строку “МОНИТОРЫ”. В поле Связь с ячейкой введите ссылку D3. В поле Количество строк списка введите число, характеризующее максимальное количество элементов в соответствующем разделе прайс-листа (в нашем случае использовано значение 100). Диалоговое окно Форматирование объекта с измененными параметрами изображено на рис. 2.10. Закройте диалоговое окно щелчком мыши на кнопке OK.

Рис. 2.10. Форматирование элемента управления Поле со списком

Îïèñàíèå ðàáîòû ýëåìåíòà óïðàâëåíèÿ Ïîëå ñî ñïèñêîì Первая строка готова к использованию. Поле со списком должно теперь отображать именованный диапазон МОНИТОРЫ, при этом порядковый номер выбранного монитора, совпадающий с номером строки диапазона, будет заноситься в ячейку D3 элементом управления Ïîëå ñî ñïèñêîì. И наоборот, если ввести любым способом в ячейку D3 целое число, то поле со списком отобразит соответствующий пункт в столбце “Наименование” раздела “Мониторы”. Разумеется, введенное число не должно быть больше, чем количества наименований в данном разделе. Первоначально в D3 ничего не содержится, поэтому никакой монитор в поле со списком не выбран. Чтобы убедиться в работоспособности строки, необходимо раскрыть список щелчком мыши на кнопке с треугольником в правой части элемента управления. В раскрывшемся списке будут отображены имеющиеся в разделе МОНИТОРЫ (и соответствующем ему диапазоне) наименования товара (рис. 2.11).

Рис. 2.11. Поле с раскрывающимся списком в действии

56 Глава 2. Excel: прайс-лист для автоматического составления заказа Выбранное при помощи мыши наименование будет автоматически помещено в поле списка, а его порядковый номер — в ячейку D3. Кроме этого, в ячейке C3 появится соответствующая выбранному наименованию цена (рис. 2.12).

Рис. 2.12. Выбор модели монитора

Èñïîëüçîâàíèå ôóíêöèè ÈÍÄÅÊÑ В ячейку C3 была помещена формула =ИНДЕКС(МОНИТОРЫ; D3; 2). Что она означает? Функция ИНДЕКС выполняет выборку из диапазона МОНИТОРЫ по порядковому номеру строки, который она берет из ячейки D3. Известно, что число 3 в эту ячейку поместил элемент управления Ïîëå ñî ñïèñêîì, поскольку в списке выбран третий по счету пункт. Цифра 2 указывает, что нужно отображать значение из второго столбца диапазона (у нас это столбец “Цена”). Ячейка B3 содержит похожую формулу, единственное отличие которой заключается в последнем аргументе функции ИНДЕКС. Цифра 1 вместо 2 означает, что ячейка отображает значение из первого столбца диапазона МОНИТОРЫ (в нашем примере это столбец “Наименование”). Но ведь ячейка не видна, она заслонена элементом управления, который и без того отображает именно это значение! Какой смысл дважды отображать одно и то же наименование, одно поверх другого? Фактически, ячейка B3 с таким же успехом могла бы быть пустой. Но находящаяся в ней формула требуется нам для того, чтобы в дальнейшем можно было бы в программном коде VBA одинаковым образом обращаться ко всем ячейкам сформированного бланка заказа. На следующем этапе построения бланка нужно “размножить” первую строку при помощи операций копирования и вставки. Покажем этот механизм на примере строки “Материнские платы”. Необходимо выделить строку “Мониторы” щелчком мыши на расположенном слева номере строки — серой области с цифрой 3 и скопировать строку в буфер обмена командой Êîïèðîâàòü из меню Ïðàâêà. Затем аналогичным образом выделить следующую (4) строку и вставить содержимое буфера обмена командой Âñòàâèòü из меню Ïðàâêà. Полученная строка будет точной копией строки “Мониторы”, ее теперь необходимо отредактировать. Вместо “Мониторы”, в ячейку A4 следует ввести: “Материнские платы”. Формулу в ячейке B4 необходимо отредактировать — вместо =ИНДЕКС(МОНИТОРЫ; D3; 1) она должна иметь вид: =ИНДЕКС(МАТЕРИНСКИЕ_ПЛАТЫ; D4; 1). Обратите внимание на тот факт, что получить доступ к ячейке B4 при помощи мыши теперь затруднительно, поскольку она заслонена элементом управления. Щелчок мыши выделит Ïîëå ñî ñïèñêîì, а не ячейку, которая находится под ним. Необходимо выделить ячейку слева или справа, а затем перейти к B4 при помощи клавиш [→] или [←]. Аналогичным образом следует исправить формулу в ячейке C4: =ИНДЕКС(МАТЕРИНСКИЕ_ПЛАТЫ; D4; 2). Далее нужно щелкнуть правой кнопкой мыши на поле со списком в ячейке B4 и выбрать в контекстном меню команду Ôîðìàò îáúåêòà. В полях на вкладке Ýëåìåíò óïðàâëåíèÿ раскрывшегося диалогового окна необходимо изменить соответствующие значения (МАТЕРИНСКИЕ_ПЛАТЫ вместо МОНИТОРЫ, и D4 вместо D3). В результате строка “Материнские платы” станет вторым действующим элементом создаваемого бланка заказа. Действуя аналогичным образом, можно создать все остальные строки: “Процессор”, “Память”, “Жесткий диск”, “Видеокарта”, “Звуковая карта” и “Корпус”. В итоге рабочий лист должен выглядеть подобно рис. 2.13.

Построение бланка заказа 57

Рис. 2.13. Все строки с раскрывающимися списками готовы к действию

Ñîçäàíèå â áëàíêå çàêàçà “áåçâàðèàíòíûõ” ñòðîê Наконец, осталось добавить две “безвариантные” строки, относящиеся к клавиатуре и мыши. Безвариантные, поскольку раздел “МЫШИ, КЛАВИАТУРЫ” в прайс-листе содержит всего одну мышь и всего одну клавиатуру (предположим, что выбора клиенту в данном вопросе не предоставляется — бывают и такие ситуации). Как поступить в этом случае? Можно просто ввести в соответствующие ячейки необходимые значения или же поместить туда ссылки на ячейки листа “PC-комплектующие”, где эти значения содержатся. Более корректный (и, заметим, более дальновидный) способ состоит в доступе к разделу прайс-листа посредством именованного диапазона и все той же функции ИНДЕКС. “Безвариантность” строки приведет лишь к тому, что вместо значения из ячейки столбца D, в качестве номера строки следует использовать просто соответствующее число. Для указания мыши следует ввести в ячейки B11 и C11 выражения =ИНДЕКС(МЫШИ_КЛАВИАТУРЫ; 1; 1) и =ИНДЕКС(МЫШИ_КЛАВИАТУРЫ; 1; 2) соответственно. Нетрудно догадаться, что для строки “Клавиатура” в ячейки B12 и C12 необходимо ввести =ИНДЕКС(МЫШИ_КЛАВИАТУРЫ; 2; 1) и =ИНДЕКС(МЫШИ_КЛАВИАТУРЫ; 2; 2).

Рис. 2.14. Бланк заказа дополнен итоговой строкой

58 Глава 2. Excel: прайс-лист для автоматического составления заказа

Ïîäñ÷åò ñóììû çàêàçà Наконец, добавим к бланку заказа итоговую строку, в которой подсчитывается цена всего компьютера в сборе. Вычисление значения суммы по всем компонентам обеспечивается формулой =СУММ(C3:C12), помещенной в ячейку C13 (рис. 2.14). Нетрудно убедиться в том, что для внесения изменений в конфигурацию готовящегося к продаже компьютера теперь достаточно всего двух щелчков мыши. Один для того, чтобы развернуть соответствующий список и второй, чтобы выбрать в списке новый компонент. Цена изменившегося компонента и новая цена всего компьютера немедленно отобразятся в бланке заказа.

Ðåøåíèå ïðîáëåìû ñîâìåñòèìîñòè âûáèðàåìûõ óñòðîéñòâ êîìïüþòåðà Любой человек, сталкивавшийся с проблемами комплектации персональных компьютеров, знает, что далеко не все компоненты представленные на мировом рынке совместимы между собой. Например, на конкретную материнскую плату можно установить отнюдь не всякий процессор. Кроме того, помимо чисто технических причин, могут иметь место и другие соображения, согласно которым определенные элементы из разных разделов прайса не должны использоваться вместе для комплектации ПК. Иными словами, бланк заказа должен проявлять некоторую разборчивость, анализировать содержащиеся в нем комбинации и не допускать появления запрещенных сочетаний компонентов. Для дальнейшего продвижения вперед в решении задачи автоматизации работы необходимо воспользоваться услугами VBA.

Ïîñòàíîâêà çàäà÷è И вновь в первую очередь требуется решить, — каким образом, и в какой момент времени должен запускаться макрос, который будет создан? Первый, приходящий на ум вариант — использовать специальный элемент управления, например, командную кнопку, при помощи которого пользователь мог бы инициировать процесс проверки выбранных вариантов на совместимость. Но в подобном решении скрываются две проблемы. Первая из них состоит в том, что анализировать придется каждый раз полный набор всех возможных сочетаний. Даже при не слишком большом числе разделов в прайсе и содержащихся в них вариантов это может привести к почти астрономическому числу анализируемых комбинаций. Вторая проблема носит интерфейсный характер. Пользователю придется выполнять проверку, то есть, приводить в действие командную кнопку, после каждого внесенного изменения. Хорошенькая автоматизация! Такой интерфейс трудно назвать дружелюбным.

Ñîçäàíèå ïðîöåäóðû Ðàñêðñïèñîê2_Èçìåíåíèå() Гораздо проще было бы анализировать только ту часть комбинаций, которая касается внесенного изменения. Поэтому выполнять анализ лучше у источника изменений — Ïîëÿ ñî ñïèñêîì. Тем более, что данный элемент управления как раз поддерживает подходящее событие. При выборе в раскрывающемся списке нового элемента происходит событие Изменение. Например, если выбрать из раскрывающегося списка первого созданного на листе Ïîëÿ ñî ñïèñêîì новое значение, то будет выполнена следующая процедура Sub Раскрсписок1_Изменение() Это позволяет, во-первых, автоматически проводить анализ комбинации именно в той части, которая подверглась изменению. И, во-вторых, при таком подходе нет нужды в глобальном рассмотрении всех комбинаций — достаточно определить несколько запретных вариантов в тех полях со списком, где такие варианты возможны.

Выпишите счет, пожалуйста… 59 Предположим, что речь идет о совместимости материнской платы и процессора. За материнскую плату у нас отвечает второе поле со списком. Второе, если до элемента управления из строки “Монитор” в данной рабочей книге поля со списком больше не использовались. Таким образом, надо в процедуре Раскрсписок2_Изменение() предусмотреть проверку — соответствует ли выбранная в данный момент материнская плата ранее выбранному процессору. Допустим, что первый пункт в разделе материнских плат совместим с первыми тремя пунктами в разделе процессоров, но не совместим с остальными тремя. Как предотвратить выбор пользователем запретной комбинации? И каким образом макрос вообще может определить, какой пункт списка выбрал пользователь? Очень просто — значения в ячейках столбца D содержат всю необходимую информацию. Как создать процедуру обработки события для элемента управления на рабочем листе, уже было рассмотрено. Щелкнем правой кнопкой мыши на поле со списком в строке “Материнская плата” и выберем в открывшемся контекстном меню команду Íàçíà÷èòü ìàêðîñ. Диалоговое окно Íàçíà÷èòü ìàêðîñ îáúåêòó закроем щелчком на кнопке Ñîçäàòü. В результате перед нами появится заготовка процедуры Раскрсписок2_Изменение(), которую остается лишь наполнить необходимым кодом, приведенным на листинге 2.1. ËÈÑÒÈÍà 2.1

Sub Раскрсписок2_Изменение() If Range("D4").Value = 1 Then If Range("D5").Value > 3 Then Range("D4").Value = 2 MsgBox "Для выбранного процессора данная плата не подходит!" End If End If End Sub Переведем исходный текст макроса на обычный язык: если в моем списке (то есть, списке материнских плат) выбран первый пункт, и если при этом в списке из строки “Процессор” выбран пункт с номером более 3, то выбрать в моем списке пункт 2 и выдать сообщение пользователю. Вернувшись в окно Excel, легко проверить действие макроса: следует выбрать четвертый пункт в строке “Процессор” и первый — в строке “Материнская плата”. Вместо первого выбранным окажется второй, а на экране появится окно сообщения, которое следует закрыть щелчком мыши на кнопке OK (рис. 2.15).

Рис. 2.15. Макрос выдает сообщение пользователю  см. также в этой главе раздел “Создание диалогового окна при помощи функции MsgBox” и в приложении раздел “Вывод сообщений (функция MsgBox)”.

Âûïèøèòå ñ÷åò, ïîæàëóéñòà… Но вот настал тот заветный миг, ради которого вообще создавался прайс-лист. Потенциальный клиент превратился во вполне реального покупателя, произнеся фразу “Выпишите счет, пожалуйста…”.

60 Глава 2. Excel: прайс-лист для автоматического составления заказа

Ïîñòàíîâêà çàäà÷è Заполненный бланк заказа теперь должен превратиться в счет. Как это сделать? У нас есть таблица компонентов компьютера с указанием их цен, а также общая сумма. Чтобы превратить все это в законченный счет, необходимо добавить заголовок и несколько обязательных реквизитов. Можно было бы сделать это, скопировав бланк в какое-то место на заранее подготовленном рабочем листе с тем, чтобы после его распечатать. Это можно сделать как вручную, так и при помощи VBA. Собственно, даже не слишком подготовленный пользователь без большого труда найдет способ это сделать. В учебно-тренировочных целях рассмотрим задачу формирования счета для клиента. Дело в том, что этот случай кажется автору подходящим для достижения сразу двух важных целей. Первая из них состоит в том, чтобы научить читателя создавать программы VBA без программирования на VBA, ведь несложные макросы в среде MS Office давным-давно можно было создавать без участия редактора Visual Basic. Это особенно важно потому, что создание макросов при помощи макрорекордера и чтение получившегося программного кода — самый легкий и доступный способ самостоятельного освоения языка VBA. Вторая цель — подготовить читателя к следующей главе, где макросы рабочих листов Excel будут работать с документами Word, а макросы документов Word — с листами Excel. Таким образом, создадим счет двумя способами. В первом случае используем макрорекордер Excel, который “сам напишет” для нас исходный текст макроса. Во втором варианте решения задачи наш макрос использует средства интеграции MS Office и создаст счет в виде документа Word.

Ýëåìåíò óïðàâëåíèÿ Êíîïêà Вначале, как обычно, позаботимся о способе запуска будущего макроса. Командная кнопка кажется в данном случае вполне уместным средством. Ее и используем. Необходимо выбрать на панели инструментов Ôîðìû элемент управления Êíîïêà и поместить его в нижней части бланка заказа (рис. 2.16).

Рис. 2.16. Бланк заказа снабжен командной кнопкой “Счет“ Создавать пустой макрос для новой кнопки необязательно. Достаточно снабдить ее надписью “Счет”. Наконец, чтобы полностью завершить подготовку бланка заказа к дальнейшим упражнениям, скроем столбец D, где содержится служебная информация — порядковые номера выбранных элементов списков: выделите столбец и выполните команду Ôîðìàò | Ñòîëáåö | Ñêðûòü. ← см. также в этой главе раздел “Как поместить на лист элемент управления”.

Ïåðâûé ñïîñîá ôîðìèðîâàíèÿ ñ÷åòà (èñïîëüçîâàíèå ìàêðîðåêîðäåðà) Создадим счет на отдельном листе в той же рабочей книге. Область таблицы с конфигурацией компьютера и столбиком “цена” каждый раз будет меняться, в то время как остальные элементы счета будут оставаться неизменными. Дату счета можно сформировать при помощи функции рабочего листа СЕГОДНЯ, а такой реквизит, как номер счета, придется вводить вручную.

Постановка задачи 61

Рис. 2.17. На отдельном рабочем листе подготовлен шаблон счета

Èñïîëüçîâàíèå ôóíêöèè ÑÅÃÎÄÍß Предполагается, что в рабочей книге уже подготовлен отдельный лист для формирования счета. Заголовок и окончание могут быть в достаточной степени произвольными, поскольку нас в данном случае интересует в первую очередь пустая область в счете, куда необходимо поместить данные из бланка заказа. Как видно из рис. 2.17, диапазон A4:C13 оставлен пустым — в него будет помещаться список компонентов и соответствующие им цены. Строка итоговой цены здесь будет формироваться заново: в ячейку C14 помещена формула =СУММ(C4:C13). Формула =СЕГОДНЯ() в ячейке C1 позволяет отображать в счете текущую дату.  см. также в приложении раздел “Системная дата и системное время”.

Итак, у нас есть сформированный заказ на листе “Бланк заказа (макро)”. Его диапазон A3:C12 содержит необходимые сведения о комплектации компьютера. Требуется скопировать эти сведения в диапазон A4:C13 на листе “Счет”. При этом хотелось бы, чтобы данная операция выполнялась по щелчку на кнопке “Счет”.

Êàê çàïèñàòü ìàêðîñ ïðè ïîìîùè ìàêðîðåêîðäåðà 1. 2.

Выполните команду Сервис | Макрос | Начать запись. В появившемся окне Запись макроса (рис. 2.18) введите в поле Имя макроса строку “Первый_способ” и щелкните на кнопке OK.

Рис. 2.18. Диалоговое окно Запись макроса 3. 4. 5. 6. 7.

Выделите на рабочем листе “Бланк заказа (макро)” диапазон A3:B12. Обратите внимание, строка 13, с общей суммой заказа, не должна попасть в выделенную область. Выполните команду Правка | Копировать. Перейдите на рабочий лист “Счет” и щелкните на ячейке A4. Выполните команду Правка | Специальная вставка. Установите переключатель Вставить в позицию значения и закройте диалоговое окно щелчком на кнопке OK.

62 Глава 2. Excel: прайс-лист для автоматического составления заказа 8.

Выполните команду Сервис | Макрос | Остановить запись.

Макрос записан. Теперь остается назначить его командной кнопке. Для этого следует щелкнуть правой кнопкой мыши на кнопке “Счет”, выбрать в контекстном меню команду Íàçíà÷èòü ìàêðîñ и выбрать макрос с именем Первый_способ. Теперь все описанные выше действия будут выполняться по щелчку мыши на кнопке “Счет” (рис. 2.19). Разумеется, нетрудно будет включить в список выполняемых действий определение области печати и вывод листа на принтер — для этого надо просто выполнить эти действия до остановки записи макроса. Такой способ формирования документа для вывода на печать не слишком совершенен, так как рабочие листы Excel все же предназначены, в первую очередь, для работы с табличными данными. В следующем разделе будет предпринята попытка создать более совершенный механизм, а пока посмотрим, что именно записал макрорекордер Excel при выполнении всех вышеописанных действий.

Рис. 2.19. Шаблон счета заполнен данными из бланка заказа по щелчку мыши на командной кнопке “Счет“

Àíàëèç êîäà àâòîìàòè÷åñêè ñîçäàííîãî ìàêðîñà Щелкните правой кнопкой мыши на кнопке “Счет”, выберите в контекстном меню команду Íàçíà÷èòü ìàêðîñ и в открывшемся диалоговом окне щелкните на кнопке Ïðàâêà. В результате

откроется окно кода с исходным текстом макроса Первый_способ (рис. 2.20).

Рис. 2.20. В окне кода отображается исходный текст макроса, записанного макрорекордером Excel

Постановка задачи 63 Как видим, макрорекордер Excel записал всю последовательность действий при помощи операторов языка VBA. Рассмотрим каждую строку в отдельности и переведем ее на обычный язык: Range("A3:C12").Select Выделить диапазон A3:C14: метод Select диапазона (объекта Range) выделяет заданную область. Selection.Copy Скопировать выделенную область в буфер обмена Windows: вызов метода Copy, принадлежащего объекту Selection, то есть выделенной области. ("Счет").Select Перейти на лист “Счет”. Все тот же метод Select, но принадлежащий на этот раз рабочему листу, делает лист текущим. Range("A4").Select Выделить ячейку A4. Selection.PasteSpecial Paste:=xlValues Выполнить в выделенной позиции специальную вставку с переключателем в положении значения. Методом PasteSpecial уже был рассмотрен и неоднократно применен — его параметр Paste определяет, что именно подлежит вставке. Этот пример демонстрирует очень простой способ изучения языка VBA. Если вы не знаете, как описать при помощи операторов VBA какую-то операцию, то следует запустить запись макроса и выполнить эту операцию вручную. Затем, после остановки записи, в окне редактора Visual Basic, можно будет прочитать описание всех выполненных действий в терминах языка VBA. ÏÐÈÌÅ×ÀÍÈÅ

Для того чтобы увидеть исходный текст произвольного макроса, необходимо выполнить команду Ñåðâèñ | Ìàêðîñ | Ìàêðîñû. В открывшемся диалоговом окне следует выделить имя требуемого макроса и щелкнуть на кнопке Ïðàâêà.

Âòîðîé ñïîñîá ôîðìèðîâàíèÿ ñ÷åòà (ìàêðîñ ëèñòà Excel ðàáîòàåò ñ äîêóìåíòîì Word) Речь пойдет о текстовом, по своей природе, документе, предназначенном для печати, и в то же время не являющимся таблицей в чистом виде. Возможности рабочего листа Excel в плане размещения и форматирования текста достаточно ограничены — документ Word предоставляет гораздо больше возможностей.

Ïîñòàíîâêà çàäà÷è Было бы заманчиво сформировать документ Word, используя в качестве первоисточника бланк заказа на рабочем листе Excel. В этом случае можно было бы придать создаваемому счету понастоящему элегантный внешний вид, не будучи стесненными ограничениями Excel. К тому же, документ Word значительно легче отредактировать, не нарушая общей его структуры, что позволяет, при необходимости, без труда внести в автоматически сформированный счет дополнительные коррективы вручную. Что ж, это не так сложно, как можно было бы предположить — в конце концов, сила MS Office не в последнюю очередь заключается в универсальности подхода к данным разного рода и сквозной интеграции инструментальных средств.

64 Глава 2. Excel: прайс-лист для автоматического составления заказа Вообще говоря, макрос рабочего листа Excel, как, собственно, любой макрос в среде MS Office, может сделать с любым приложением или документом Office все, что угодно. Он может работать с документом Word, не запуская сам Word, или же наоборот, открыть окно MS Word и там создать любой документ. При помощи кода VBA можно заполнить документ любым текстом и как угодно его отформатировать. Однако зачастую в таком глобальном подходе нет практического смысла. Все же, редактировать и форматировать документы Word удобнее всего при помощи функций самого Word. Поэтому в случаях, подобных рассматриваемой задаче в этом разделе, целесообразно придерживаться принципа золотой середины — образец счета без полезной информации можно создать в среде Word, а затем, используя его в качестве заготовки, дополнять конкретными данными при помощи программного кода.

Ñîçäàíèå îáðàçöà ñ÷åòà â Word Создадим вначале образец счета — документ Word, содержащий неизменяемые элементы счета. Для этого следует запустить MS Word, и создать новый документ. Заголовок счета может быть произвольным (рис. 2.21). В первую очередь нас интересует механизм доступа к документу посредством программного кода VBA.

Рис. 2.21. Образец счета, подготовленный в текстовом процессоре Word Задача заключается в том, чтобы вставить в документ изменяемые реквизиты счета (номер и дату), а также дополнить счет содержимым бланка заказа с рабочего листа Excel. Вначале необходимо вставить в строку “Счет №...” два поля — поле типа “переменная документа” и поле “дата”. Êàê âñòàâèòü ïîëÿ â äîêóìåíò Word 1. Поместите курсор ввода в позицию между “Счет №” и “от”. 2. В меню Вставка выберите команду Поле.

Постановка задачи 65

Рис. 2.22. Диалоговое окно Поле В диалоговом окне Ïîëå выберите в списках Категории и Поля пункты Автоматизация документа и DocVariable соответственно (см. рис. 2.22). 4. Добавьте в строку описания поля в нижней части окна имя “Номер_счета”, как показано на рисунке. 5. Закройте диалоговое окно щелчком мыши на кнопке OK. 6. Поместите курсор ввода в позицию после слова “от”. 7. Вновь открыв окно Поле, выберите в списках Категории и Поля пункты Дата и время и Date соответственно. 8. Щелкнув на кнопке Параметры, выберите в окне Параметры поля один из форматов отображения даты. 9. Закройте оба диалоговых окна щелчками на кнопках Добавить и OK. Далее следует решить, каким образом можно попасть из макроса VBA в нужную позицию документа. Предположим, что данные из бланка заказа должны быть помещены между строками “Счет №…” и “Счет действителен…”. Самый простой метод ориентации в документе состоит в привязке к конкретному абзацу. На рис. 2.21 видно, что такой абзац намеренно оставлен пустым, — пятый по счету. Запомним этот факт. Осталось сохранить документ. В этом примере документ сохранен под именем C:\blank.doc. Заготовка готова.

3.

Ñîçäàíèå ìàêðîñà ðàáî÷åãî ëèñòà Excel äëÿ ðàáîòû ñ äîêóìåíòîì Word Теперь можно создать макрос рабочего листа Excel, который будет работать с этим документом. ÏÐÈÌÅ×ÀÍÈÅ

Прежде чем приступить к созданию соответствующего кода, необходимо будет выполнить одну операцию, которая на языке программистов называется “задать ссылку на библиотеку”. Дело в том, что технология Automation (ранее она называлась OLE Automation) подразумевает, что VBAкод одного из приложений Office может обращаться к объектам другого приложения посредством библиотеки типов, ссылка на которую должна быть доступна упомянутому коду. Например, чтобы макрос в рабочей книге Excel мог обращаться к объектам Microsoft Word 2000, к нему нужно подключить библиотеку Microsoft Word 9.0 Object Library. Понятно, что если речь идет о Word другой версии, то название библиотеки может немного отличаться. Например, в пакете MS Office 97 эта библиотека называется Microsoft Word 8.0 Object Library. Сама процедура создания макроса несколько отличается от тех, которые были рассмотрены ранее.

66 Глава 2. Excel: прайс-лист для автоматического составления заказа Вернитесь в среду Excel и выполните команду Ñåðâèñ | Ìàêðîñ | Ìàêðîñû. В диалоговом окне Ìàêðîñ введите в поле Èìÿ ìàêðîñà строку “Второй_способ” — пусть это будет имя нашего будущего макроса. Щелкните на кнопке Ñîçäàòü. В результате откроется хорошо уже нам знакомое окно редактора Visual Basic, при этом в окне кода будет создана пустая процедура с заданным именем. Прежде чем вводить в нее код, задайте ссылку на библиотеку объектов Word. Êàê çàäàòü ññûëêó íà áèáëèîòåêó îáúåêòîâ Word â ñðåäå MS Excel 1. Находясь в окне редактора Visual Basic, выполните команду Tools | References (Сервис | Ссылки). 2. В списке Available References (Доступные ссылки) найдите пункт Microsoft Word 9.0 Object Library. 3. Установите флажок напротив этого пункта. 4. Закройте диалоговое окно щелчком мыши на кнопке OK, теперь можно приступать к вводу исходного текста процедуры (листинг 2.2).  см. также в гл. 3 раздел “Как задать ссылку на библиотеку объектов Excel в среде MS Word”, в гл. 6 раздел “Ссылка на библиотеку объектов MS Access” и в гл. 9 раздел “Как подключить к Excel библиотеку типов Outlook”. ËÈÑÒÈÍÃ 2.2

Sub Второй_способ() Dim WD As Word.Application Dim BillTable As Table Dim TRow, TCol As Integer Set WD = CreateObject("Word.Application") WD.Visible = True WD.Documents.Open "C:\blank.doc" With WD.ActiveDocument .Variables("Номер_счета").Value = "1" .Fields.Update End With Set BillTable = WD.ActiveDocument.Tables.Add _ (WD.ActiveDocument.Paragraphs(5).Range, 12, 3) For TCol = 1 To 3 For TRow = 1 To 11 BillTable.Cell(TRow, TCol).Range = _ ActiveSheet.Cells(TRow + 1, TCol).Value Next TRow Next TCol BillTable.Cell(12, 1).Range = "Всего по счету" BillTable.Cell(12, 3).Range = ActiveSheet.Range("C13").Value BillTable.AutoFormat Format:=wdTableFormatContemporary End Sub

Постановка задачи 67 Чтобы связать созданный макрос с кнопкой Ñ÷åò, необходимо вновь выбрать в ее контекстном меню команду Íàçíà÷èòü ìàêðîñ. И в списке отобразившегося диалогового окна Íàçíà÷èòü ìàêðîñ îáúåêòó нужно выбрать пункт “Второй_способ”, завершив операцию щелчком мыши на кнопке OK. Теперь кнопка Ñ÷åò готова к действию — достаточно щелкнуть на ней и макрос Второй_способ будет выполнен (рис. 2.23). Àíàëèç ïðîöåäóðû äëÿ ðàáîòû ñ äîêóìåíòîì Word Посмотрим внимательно на текст процедуры VBA. Dim WD As Word.Application Dim BillTable As Table Dim TRow, TCol As Integer В разделе описания переменных кое-что понятно и без дополнительных объяснений. Переменные TRow и TCol предназначены для хранения целочисленных значений. Переменная BillTable — это таблица Word. А вот переменная WD, тип которой Word.Application, есть ни что иное, как… сам Word. Точнее, эта переменная станет приложением Word, когда выполнится оператор: Set WD = CreateObject("Word.Application")  см. также в приложении раздел “Объектные переменные”.

Рис. 2.23. Макрос рабочего листа Excel создал счет в документе Word Строка WD.Visible = True делает Word видимым. Если бы не она, то ни окно Word на рабочем столе, ни его кнопка на панели задач не появились бы. Хотя это не помешало бы выполнению всех последующих операций. Далее к семейству документов, открытых в окне Word, добавляется заготовка C:\blank.doc: WD.Documents.Open "C:\blank.doc"

68 Глава 2. Excel: прайс-лист для автоматического составления заказа Теперь этот документ стал текущим и его свойства доступны через объект ActiveDocument. Следующий код задает значение “1” для переменной документа с именем “Номер_счета” и принудительно обновляет все поля документа: With WD.ActiveDocument .Variables("Номер_счета").Value = "1" .Fields.Update End With Создание таблицы в документе Word и ее автоформатирование Настала пора создать в документе таблицу, в которую будут помещены полученные данные: Set BillTable = WD.ActiveDocument.Tables.Add _ (WD.ActiveDocument.Paragraphs(5).Range, 12, 3) ÏÐÈÌÅ×ÀÍÈÅ

Пробел и знак подчеркивания в тексте программы означают просто перенос на другую строку. При вводе этого оператора в окне кода весь оператор можно вводить целиком, игнорируя пробел и знак подчеркивания — “ _”. Параметры метода Add, принадлежащего семейству таблиц текущего документа определяют, что таблица размером 12х3 должна быть вставлена в позиции 5-го абзаца. Наконец, заполним таблицу данными из ячеек бланка заказа. Для этого потребуется перебрать ее по строкам и столбцам при помощи операторов цикла и двух переменных. Переменная TCol в нашем примере отвечает за столбцы, а TRow — за строки: For TCol = 1 To 3 For TRow = 1 To 11 BillTable.Cell(TRow, TCol).Range = _ ActiveSheet.Cells(TRow + 1, TCol).Value Next TRow Next TCol  см. в приложении раздел “Цикл For … Next”.

Здесь в одной строке встречаются ячейки текущего рабочего листа Excel (ActiveSheet.Cells) и ячейки таблицы Word (BillTable.Cell). Как видим, ничто не мешает им взаимодействовать друг с другом точно так же, как если бы это были объекты одного приложения. Это и есть технология Automation в действии. В ячейку последней строки таблицы записывается строка “Всего по счету” и значение из ячейки C13 бланка заказа: BillTable.Cell(12, 1).Range = "Всего по счету" BillTable.Cell(12, 3).Range = ActiveSheet.Range("C13").Value Последняя строка макроса BillTable.AutoFormat Format:=wdTableFormatContemporary вызывает метод AutoFormat для таблицы в документе Word. Действие этого метода аналогично команде Àâòîôîðìàò меню Òàáëèöà приложения Word. Параметру Format было присвоено значение wdTableFormatContemporary, что соответствует формату Ñîâðåìåííûé. Чтобы узнать имена констант, соответствующих другим вариантам автоформата, выделите в тексте макроса слово AutoFormat и нажмите клавишу [F1].

Постановка задачи 69 ÏÐÈÌÅ×ÀÍÈÅ

Выше уже упоминался тот факт, что библиотека объектов Word может называться по-разному в разных версиях пакета MS Office. Следствием этого факта проявляется следующий эффект: если перенести готовую рабочую книгу с макросами, для которых задана ссылка на такую библиотеку, в среду другой версии MS Office, то макрос “откажется работать” — ссылку на библиотеку объектов Word для него необходимо будет задать заново, удалив при этом старую ссылку.

Óñîâåðøåíñòâîâàíèÿ ìàêðîñà ðàáî÷åãî ëèñòà Excel äëÿ ðàáîòû ñ äîêóìåíòîì Word Ïåðâîå. Созданный макрос вполне работоспособен, но нуждается в некотором усовершенствовании. Прежде всего, необходимо автоматически генерировать номер счета. С этой целью в ячейки справа от кнопки Счет (в рассматриваемом примере это ячейки E15 и F15) были помещены значения “№” и “1000”. Предположим, что нумерация счетов должна начаться с 1000. Теперь можно использовать число, содержащееся в ячейке F15, в качестве счетчика:

ActiveSheet.Range("F15").Value = ActiveSheet.Range("F15").Value + 1 При генерации каждого нового счета это число будет увеличиваться на единицу, и вставляться в документ Word в позиции, определяемой переменной Номер_счета: With WD.ActiveDocument .Variables("Номер_счета").Value = _ ActiveSheet.Range("F15").Value .Fields.Update End With Âòîðîå. Далее, уберем строку WD.Visible = True, благодаря чему окно приложения Word не будет отображаться на экране автоматически. Вместо этого предоставим пользователю возможность выбора, организовав этот выбор при помощи условного оператора If…Then…Else… и функции MsgBox:

If MsgBox("Сохранить счет и выйти?", vbYesNo, "Счет") = _ vbYes Then ... Else ... End If

Рис. 2.24. Макрос запрашивает пользователя о дальнейшем ходе выполнения процедуры Ñîçäàíèå äèàëîãîâîãî îêíà ïðè ïîìîùè ôóíêöèè MsgBox Строки "Сохранить счет и выйти?" и "Счет" определяют надпись и заголовок окна сообщения MsgBox (рис. 2.24). Параметр vbYesNo задает набор кнопок в диалоговом окне — это будут кнопки Äà и Íåò. В результате анализа значения, которое вернет функция MsgBox, будет выполняться код после оператора If… (пользователь выбрал Äà) или же после Else… (пользователь выбрал Íåò).  см. также в приложении разделы “Вывод сообщений (функция MsgBox)” и “Конструкция If … Then … Else … End If”.

70 Глава 2. Excel: прайс-лист для автоматического составления заказа ← см. также в этой главе раздел “Создание процедуры Раскрсписок2_Изменение()” Листинг 2.1. Òðåòüå. Пусть документ Word автоматически выводится на печать вне зависимости от выбора пользователя:

WD.ActiveDocument.PrintOut Copies:=2 Параметр Copies определяет количество копий для печати. ×åòâåðòîå. Затем, если пользователь согласен с тем, что документ надо сохранить и выйти из

него (при этом никаких признаков Word так и не появится на экране), документ будет записан на диск под именем, в состав которого входит номер счета: WD.ActiveDocument.SaveAs "C:\BILL" + _ Str(ActiveSheet.Range("F15")) Функция Str вернет число из ячейки-счетчика, преобразованное в строку, в результате для первого счета имя файла будет C:\BILL1001.doc. Затем приложение Word, хранящееся в переменной WD, будет выгружено из памяти: WD.Quit В случае если пользователь не согласился с предложением “Сохранить счет и выйти?”, будет выполняться часть условной конструкции после оператора Else… WD.Visible = True WD.Activate WD.WindowState = wdWindowStateMaximize Свойство Visible отвечает за видимость объекта — в результате присваивания ему значения True приложение Word перестанет быть “невидимкой”. Метод Activate делает окно Word активным, то есть выводит его на передний план экрана. Наконец, свойство WindowState определяет состояние окна: значение wdWindowStateMaximize разворачивает окно на весь экран. В результате “несогласный” пользователь получает возможность самому решать, что делать с готовым счетом дальше. В итоговом варианте макрос приобретает следующий вид, как показано на листинге 2.2. ËÈÑÒÈÍÃ 2.3

Sub Второй_способ() Dim WD As Word.Application Dim BillTable As Table Dim TRow, TCol As Integer Set WD = CreateObject("Word.Application") WD.Documents.Open "C:\blank.doc" ActiveSheet.Range("F15").Value = _ ActiveSheet.Range("F15").Value + 1 With WD.ActiveDocument .Variables("Номер_счета").Value = _ ActiveSheet.Range("F15").Value .Fields.Update End With

Постановка задачи 71 Set BillTable = WD.ActiveDocument.Tables.Add _ (WD.ActiveDocument.Paragraphs(5).Range, 12, 3) For TCol = 1 To 3 For TRow = 1 To 11 BillTable.Cell(TRow, TCol).Range = _ ActiveSheet.Cells(TRow + 1, TCol).Value Next TRow Next TCol BillTable.Cell(12, 1).Range = "Всего по счету" BillTable.Cell(12, 3).Range = ActiveSheet.Range("C13").Value BillTable.AutoFormat Format:=wdTableFormatContemporary WD.ActiveDocument.PrintOut Copies:=2 If MsgBox("Сохранить счет и выйти?", vbYesNo, "Счет") = _ vbYes Then WD.ActiveDocument.SaveAs _ "C:\BILL" + Str(ActiveSheet.Range("F15")) WD.Quit Else WD.Visible = True WD.Activate WD.WindowState = wdWindowStateMaximize End If End Sub

Глава 3 Ïîâåðêà ãàðìîíèè Word àëãåáðîé Excel Êîå-÷òî î ñîâðåìåííîì äîêóìåíòîîáîðîòå Документооборот современного офиса отличается, прежде всего, многообразием. Давным-давно, во времена MS DOS и командных оболочек типа Norton Commander, управление большим количеством текстовых файлов не представляло серьезной проблемы. В самом крайнем случае, когда никакие другие способы найти нужный текст уже не помогали, отчаявшийся пользователь мог прибегнуть к сочетанию клавиш [Alt] + [F7] и ввести строку символов, которая, предположительно, должна содержаться в искомом файле. Если, например, он искал текст договора с фирмой “АБВГД”, то такой способ позволял найти в текущем каталоге все текстовые файлы, содержащие строку “АБВГД” — почти наверняка среди них находился и искомый документ. В наше время простой текстовый файл все реже используется в качестве “хранилища” документа. Формат файла документа Word предоставляет неизмеримо более широкие возможности в плане форматирования текста, таблиц, рисунков и т.п. Документ Word позволяет проверить грамматику и орфографию, связать себя с графическим файлом или таблицей Excel — нет смысла перечислять все возможности Word. И без того всем понятно, что времена простого DOS-текста в качестве офисного документа безвозвратно уходят. Однако каждый, кто по роду деятельности имеет дело с большим количеством документов Word, знает, что найти нужный документ порою бывает очень непросто. Тут не поможет командная оболочка и сочетание клавиш [Alt] + [F7] — текст содержится в файлах Word далеко не в простом виде. Какие существуют пути для управления большим числом документов Word, или, если уж на то пошло, вообще документов? Иногда документ может представлять собой лист рабочей книги Excel, слайд из презентации PowerPoint или HTML-страницу. Прежде всего, конечно, классификация и структурирование. Можно создать отдельные папки (каталоги) для договоров, актов, накладных и т.д. Внутри каждой такой папки можно создать соответствующие вложенные папки — например, договора можно разбить по типам (купли-продажи, подряда и т.д.) и по контрагентам — завести, например, для каждой фирмы-контрагента отдельную папку с договорами. Теоретически, такой подход должен помочь пользователю справиться с многообразием офисных документов. На практике это верно лишь отчасти. Проблема в том, что людям свойственно не только ошибаться, — им еще свойственно спешить. Стройная система классификации хороша до тех пор, пока она стройна на все 100 процентов. Если хотя бы время от времени помещать очередной документ не в ту папку, что требует классификация, или не сохранять его вовсе, то система теряет стройность. Работа есть работа — если срочно требуется подготовить какой-то договор или письмо, его делают из аналогичного документа, сделанного ранее. В спешке же первоисточник могут и не сохранить. И вот, спустя время, тот же самый или другой пользователь сталкивается с ситуацией: искомого документа в соответствующей папке нет. Что это значит? Такого документа вообще не было, или он кем-то ошибочно удален или помещен в другую папку? Неизвестно. И вот что обидно — такого документа, в самом деле, могло не быть вовсе, но как в этом теперь убедиться? Надо побеспокоиться об исполнительской дисциплине. Проинструктируем всех сотрудников еще раз! Это все равно, что заставлять всех сотрудников всегда писать красивым “печатным” почерком. Никакие дисциплинарные меры (даже угроза “расстрела!”) не принесут 100%-го успеха, и в то же

74 Глава 3. Поверка гармонии Word алгеброй Excel время чисто техническое решение (печатная машинка или компьютер с принтером) полностью снимет проблему раз, и навсегда. Посмотрим, какие технические решения из арсенала MS Office помогут нам “укротить стихию” современного документооборота.

Èç äîêóìåíòà Word â òàáëèöó Excel Упорядоченность и единообразие, свойственные Excel-таблице, представляются хорошим средством в противоположность трудно управляемой среды документов Word. Несомненно, если бы удалось скрестить эти две “стихии”, результат обладал бы многими интересными качествами. Можно подступиться к решению этой задачи с двух направлений.

Ïîñòàíîâêà çàäà÷è Ïåðâîå íàïðàâëåíèå (оно будет рассмотрено в этом разделе) представляет собой такие методы “скрещивания”, благодаря которым документы Word продолжают существовать сами по себе, никак не интегрируясь с таблицей Excel, но при этом сами документы, так же как их важнейшие реквизиты, автоматически учитываются в своеобразной документной базе данных, роль которой призвана сыграть таблица Excel. Никаких ограничений на структуру или состав документа при этом не накладывается — это могут быть абсолютно произвольные документы самого различного происхождения. Рабочая книга Excel позволит автоматизировать учет документов (а также их версий) и обеспечит возможности поиска и доступа. Âòîðîå íàïðàâëåíèå, разработкой которого стоит заняться, и которое будет рассмотрено позднее, во второй части главы, меняет вектор решения на противоположный. Если “двигаться” от таблицы Excel к документу Word, то отпадает нужда в сохранении собственно документа. Все реквизиты документа могут прекрасно храниться в таблице, что позволяет выполнять групповые операции над множеством документов. И при необходимости любой документ может быть восстановлен по своим реквизитам.

Ïîäãîòîâêà òàáëèöû äîêóìåíòîâ Вначале необходимо подготовить рабочую книгу Excel, которая будет играть роль документной базы данных. Для удобства работы с документами их следует разбить на категории. Пусть каждой категории будет посвящен отдельный лист рабочей книги. Предположим, в офисе ООО “Универсал” наиболее часто работают с такими документами, как договор, акт и доверенность. Некоторое число документов других типов будут отнесены к категории “Прочее”. Таким образом, нужно создать рабочую книгу, состоящую из четырех листов: “Договор”, “Акт”, “Доверенность” и “Прочее” (рис. 3.1). Конечно, это деление полностью условно. Ничто не мешает расширить этот набор или использовать совершенно другие категории — все зависит от конкретных обстоятельств. Впрочем, категория “Прочее” должна присутствовать обязательно — в нее будут попадать не только те документы, которые не соответствуют ни одной из других категорий, но и документы, принадлежность которых макросу не удастся правильно распознать.

Рис. 3.1. Рабочая книга для регистрации документов может состоять из нескольких однотипных листов

Из документа Word в таблицу Excel 75 Строение этих листов должно быть однотипным: пусть первая строка представляет собой заголовок таблицы. Столбцы A, B и C будут использоваться для хранения ключевых сведений о документе. В столбец “Ссылка” будет помещаться гиперссылка на документ, позволяющая открыть его простым щелчком мыши. Столбец “Описание” предназначен для хранения некоторых ключевых реквизитов документа. Вначале, для простоты, будем помещать туда просто его первый абзац. Наконец, в ячейках столбца “Текст” будет храниться текст документа, или, по крайней мере, его часть. Наличие фрагментов текста на листе позволит, при необходимости, искать документ по вхождению строки символов, используя функции поиска Excel. Названия столбцов условные. Будем обращаться к столбцам по их номерам, поэтому, строго говоря, можно обойтись вообще без заголовков. ÏÐÈÌÅ×ÀÍÈÅ

Единственный параметр, значение которого не терпит никакой двусмысленности — имя файла рабочей книги и путь к нему. Будущий макрос-архивариус должен обнаружить рабочую книгу по указанному пути. Предположим, что книга сохранена под именем DocTable.xls в корневом каталоге диска C:, таким образом, полный путь к файлу можно записать, как строку C:\DocTable.xls.

Ïðèâÿçêà ìàêðîñà ê êîìàíäå Word Теперь перейдем в окно MS Word. Как всегда, перед созданием макроса необходимо подумать о способе его запуска. Текущий документ Word с точки зрения VBA представлен объектом ActiveDocument, который поддерживает ряд событий. Мжно было бы выполнять макрос по такому событию, как, скажем, Close (закрытие документа). Кроме того, запуск макроса можно обеспечить по нажатию сочетания клавиш. Но в данном случае обычные методы непригодны: нет смысла создавать новую запись в таблице документов каждый раз, когда закрывается документ — в процессе работы над документом, его, может быть, придется открыть и закрыть много раз. Запуск по нажатию сочетания клавиш плох тем, что требует от пользователя специальных усилий — пользователь должен не забыть это сделать. К счастью, существует возможность “привязать” макрос к команде Word. Такой макрос будет выполняться, как при выборе в меню соответствующей команды, так и при щелчке на кнопке панели инструментов, представляющей ту же команду Word. Конечно, сама команда Word также будет при этом выполняться. Наиболее подходящими представляются команды Ñîõðàíèòü, Ñîõðàíèòü êàê… и Ïå÷àòü. Если при выполнении одного из этих действий наш архивариус будет включать сведения о документе в таблицу, то с одной стороны, не будет упущен ни один представляющий интерес документ, а с другой — не будет перегружена таблица слишком большим числом излишних версий одного документа. Какое из действий наилучшим образом подходит для этого, читатель решит сам, а в данном примере в качестве команды-носителя используем команду Ñîõðàíèòü. Этой команде Word соответствуют следующие элементы интерфейса Word: • одноименная команда меню Ôàéë; • кнопка на панели инструментов Ñòàíäàðòíàÿ; • сочетание клавиш [Shift] + [F12]. В результате, в любом случае, будет выполнена команда FileSave (Ñîõðàíèòü), а вместе с ней и будущй макрос.

Êàê çàìåíèòü êîìàíäó Word 1.

Командой Сервис | Макрос | Макросы откройте диалоговое окно Макрос (рис. 3.2).

76 Глава 3. Поверка гармонии Word алгеброй Excel

Рис. 3.2. Диалоговое окно Макрос позволяет отобразить все доступные макросы, где бы они ни находились 2.

3.

4.

5. 6.

Здесь доступны макросы Word, хранящиеся в различных местах. В списке Макросы из, в нижней части диалогового окна, выбран пункт Активных шаблонов. Это означает, что в окне отображаются макросы, хранящиеся в присоединенных к документу шаблонах, и в первую очередь, конечно, макросы из общего шаблона Normal.dot (Обычный). Общий шаблон — лучшее место для хранения нашего макроса, поскольку при этом не потребуется помещать никакой VBA-код в документы, и в то же время макрос будет всегда “под рукой”. Разверните список Макросы из и выберите пункт Команд Word. В окне отобразится список команд, среди которых надо выбрать подходящую команду. Найдите команду FileSave — рядом с ней находится команда FileSaveAs (Сохранить как). Команде Печать соответствует пункт Print (рис. 3.3). Просмотр команд Word потребовался нам здесь только для того, чтобы выбрать подходящую команду и запомнить ее имя. Команды Word, как таковые, нельзя изменить при помощи редактора Visual Basic — кнопка Изменить, как видно на рис. 3.3, отключена. Но не беда — можно создать одноименную команду, которая будет выполнять те же функции — Word автоматически заменит ею свою команду. Снова разверните список Макросы из и выберите пункт Normal.dot (общего шаблона) или Обычный.dot (общего шаблона). Введите в поле Имя строку FileSave и щелкните на кнопке Создать. В результате, в окне кода редактора Visual Basic будет создан одноименный макрос (рис. 3.4), который автоматически подменит соответствующую команду Word.

ÏÐÈÌÅ×ÀÍÈÅ

Понятно, что для “привязки” макроса к другой команде Word, здесь необходимо ввести в поле Имя другую строку. Например, если ввести строку FileSaveAs, то будущий макрос будет “привязан” к команде Сохранить как и будет выполняться только тогда, когда пользователь впервые сохраняет вновь созданный документ или же сохраняет существующий документ под новым именем.

Из документа Word в таблицу Excel 77

Рис. 3.3. Окно Макрос отображает список команд Word Как видно на рис. 3.4, макрос уже содержит одну строку кода, которая, собственно, и выполняет операцию “Сохранить” по отношению к текущему документу. Теперь можно дополнить исходный текст макроса своим кодом, который будет выполняться каждый раз, когда пользователь сохраняет документ тем или иным способом: при помощи команды меню, кнопки панели инструментов или сочетания клавиш.

Рис. 3.4. Макрос, одноименный с одной из команд Word, автоматически содержит соответствующий код ÏÐÅÄÓÏÐÅÆÄÅÍÈÅ

Конечно, строку ActiveDocument.Save удалять из текста процедуры ни в коем случае нельзя, иначе документы Word перестанут сохраняться на диске.

Ñîçäàåì ìàêðîñ-àðõèâàðèóñ Word-äîêóìåíòîâ Поскольку работа выполняется в среде документа Word и при этом предполагается выполнять действия над объектами рабочей книги Excel, прежде всего, необходимо задать ссылку на соответствующую библиотеку, иначе VBA “не поймет” некоторых слов в нашей программе. Подобная операция уже выполнялась, когда осуществлялся доступ к документу Word из листа рабочей книги Excel. Теперь возникла необходимость задать обратную ссылку. ← см. также в гл. 2 раздел “Как задать ссылку на библиотеку объектов Word в среде MS Excel”, в гл. 6 раздел “Ссылка на библиотеку объектов MS Access” и в гл. 9 раздел “Как подключить к Excel библиотеку типов Outlook”.

78 Глава 3. Поверка гармонии Word алгеброй Excel Êàê çàäàòü ññûëêó íà áèáëèîòåêó îáúåêòîâ Excel â ñðåäå MS Word 1. 2. 3.

Находясь в окне Word, откройте окно редактора Visual Basic командой Сервис | Макрос | Редактор Visual Basic. В окне редактора Visual Basic выполните команду Сервис | Ссылки (Tools | References). Найдите в списке пункт Microsoft Excel 9.0 Object Library.

ÏÐÈÌÅ×ÀÍÈÅ

в другой версии MS Office имя этой библиотеки может немного отличаться, например, в MS Office 97 она называется Microsoft Excel 8.0 Object Library. 4. 5.

Установите флажок напротив этого пункта. Закройте диалоговое окно щелчком мыши на кнопке OK.

Итак, все готово для создания задуманного макроса. Чтобы открыть окно программного кода с исходным текстом макроса, можно воспользоваться командой Ñåðâèñ | Ìàêðîñ | Ìàêðîñû. В диалоговом окне Ìàêðîñ необходимо выделить имя требуемого макроса (в данном случае это FileSave), и щелкнуть на кнопке Èçìåíèòü. Остается ввести исходный текст. Åùå ðàç íàïîìíèì: автоматически созданная строка ActiveDocument.Save должна остаться без изменений. Чтобы лучше понять принцип действия макроса, вначале попробуем реализовать максимально упрощенный вариант, своего рода “действующую модель” (листинг 3.1). ËÈÑÒÈÍÃ 3.1

Sub FileSave() ActiveDocument.Save Dim EX As Excel.Workbook Set EX = GetObject("С:\DocTable.xls") With EX.ActiveSheet .Cells(2, 3).Value = ActiveDocument.Range(0, 200).Text .Cells(2, 2).Value = ActiveDocument.Paragraphs(1).Range.Text .Hyperlinks.Add .Cells(2, 1), ActiveDocument.FullName End With EX.NewWindow EX.Save Set EX = Nothing End Sub

Îïèñàíèå ðàáîòû ìàêðîñà-àðõèâàðèóñà Word-äîêóìåíòîâ Предположим, что файл рабочей книги DocTable.xls существует и находится в корневом каталоге диска C:. Откроем в окне Word какой-нибудь документ, пусть, например, это будет договор подряда (рис. 3.5).

Из документа Word в таблицу Excel 79

Рис. 3.5. В окне Word открыт документ Сохраним документ любым способом, например, нажатием клавиш [Shift] + [F12]. Затем, запустив Excel и открыв рабочую книгу C:\DocTable.xls, посмотрим на ее содержимое. Пока не имеет значения, на каком листе книга открылась. Как видно, некоторые сведения о документе оказались записанными в таблицу (рис. 3.6).

Рис. 3.6. Таблица содержит сведения о сохраненном документе Синяя строка в ячейке столбца “Ссылка” (она видна не полностью) представляет собой полный путь к файлу — гиперссылку на документ. При выборе этой строки мышью ее указатель приобретает вид руки с вытянутым указательным пальцем, а одиночный щелчок на такой ссылке приведет к открытию документа (разумеется, если файл документа по-прежнему находится в указанном месте). В ячейку столбца “Описание” помещен просто первый абзац документа, а в ячейке столбца “Текст” содержится первые 200 символов текста. Перейдя в режим редактирования содержимого ячейки C2, на экране появятся все, оодержащиеся в ней символы (рис. 3.7).

Рис. 3.7. Текст, содержащийся в ячейке, можно увидеть полностью, щелкнув на ней мышью

Àíàëèç êîäà äåéñòâóþùåé ìîäåëè àðõèâàðèóñà Word-äîêóìåíòîâ â Excel Прежде чем перейти от “действующей модели” к полноценному макросу, разберем исходный текст. Dim EX As Excel.Workbook Благодаря этой строке будет создана переменная типа “рабочая книга Excel” с именем EX.

80 Глава 3. Поверка гармонии Word алгеброй Excel Àâòîìàòè÷åñêîå îòîáðàæåíèå íà ýêðàíå áèáëèîòåêè îáúåêòîâ Excel Кстати, ввод этой строки — хороший способ убедиться в наличии ссылки на библиотеку объектов Excel. После ввода символов As и пробела появится окно подсказки со списком всех доступных типов. Если ссылка на библиотеку объектов Excel задана, то среди прочих в списке будет присутствовать слово Excel. Если выделить в списке этот пункт и ввести точку, то слово будет помещено в строку, а рядом появится следующий список. Этот список и есть библиотека объектов Excel. Остается найти слово Workbook. Конечно, можно просто ввести с клавиатуры Excel.Workbook, не обращая внимания на происходящее на экране. Set EX = GetObject("С:\DocTable.xls") С выполнением этой строки объявленная выше переменная EX “наполнится содержанием”. Теперь она представляет рабочую книгу Excel, хранящуюся в файле С:\DocTable.xls. Она обладает всеми свойствами и методами рабочей книги и с ней можно обращаться точно таким же образом, как если бы это была открытая рабочая книга DocTable, и к ней обращались в среде Excel из программного кода. Единственное отличие: в редакторе Visual Basic рабочей среды Excel этот объект назывался бы ActiveWorkbook или ThisWorkbook, или же просто его имя было бы опущено, поскольку по умолчанию в Excel речь всегда идет о текущей книге. В данном случае этот объект называется EX. Поскольку объект EX был создан таким образом, что он не включает в себе само приложение (программу) Excel — окно Excel, даже скрытое, на экране присутствовать не будет — макрос Word со всем “справится сам”. With EX.ActiveSheet .Cells(2, 3).Value = ActiveDocument.Range(0, 200).Text .Cells(2, 2).Value = ActiveDocument.Paragraphs(1).Range.Text .Hyperlinks.Add .Cells(2, 1), ActiveDocument.FullName End With Макрос открыл книгу на активном листе, пока не имеет значения — на каком. Теперь с этим объектом, то есть с текущим рабочим листом (EX.ActiveSheet), будет выполнено несколько действий. Речь пойдет о ячейках второй строки. Индексы в скобках, после слова Cells, обозначают, соответственно, номер строки и номер столбца. В ячейку третьего столбца будут помещены первые 200 символов текста из активного документа Word, начиная с позиции 0. Ячейка второго столбца получит в качестве значения текст первого абзаца документа (Paragraphs(1).Range.Text). Затем в семейство гиперссылок (Hyperlinks) рабочей книги будет добавлена при помощи метода Add гиперссылка на текущий документ Word. Первый параметр метода Add указывает на ячейку листа, в которую нужно ввести гиперссылку, а второй представляет собой полное имя файла текущего документа. Далее следуют завершающие строки макроса: EX.NewWindow EX.Save Set EX = Nothing Метод NewWindow создает экранное окно для книги — без этой строки книга останется скрытой при последующем ее открытии при помощи Excel и потребуется отобразить окно книги посредством команды Îêíî | Îòîáðàçèòü. Метод Save сохраняет книгу с внесенными в нее изменениями. Îñâîáîæäåíèå ñèñòåìíîé ïàìÿòè êîìïüþòåðà Наконец, после присвоения нашей переменной-объекту значение Nothing, она освобождается от своего содержимого: если этого не сделать, то при каждом очередном выполнении макроса занятая переменной память не будет освобождаться, что рано или поздно приведет к недостатку памяти в системе.

Из документа Word в таблицу Excel 81

Óñîâåðøåíñòâîâàíèÿ äåéñòâóþùåé ìîäåëè äëÿ ñîçäàíèÿ ìàêðîñààðõèâàðèóñà Word-äîêóìåíòîâ Итак, действующая модель сделала свое дело, и было показано, каким образом документ Word может записать свои данные в таблицу Excel, не прибегая к помощи самого Excel. Чтобы этот механизм можно было использовать на практике, необходимо решить ряд проблем. Ïåðâîå: при каждом очередном вызове макроса, то есть при сохранении текущего документа Word, запись в таблицу должна производиться в очередную свободную строку. Это означает, что вместо 2 в выражениях вида .Cells(2, ...) необходимо использовать некоторую переменную, которая принимала бы последовательно возрастающие значения. Можно использовать специальную целочисленную переменную, назовем ее Count, при помощи которой можно было бы перебирать строки таблицы и останавливать этот процесс при обнаружении свободной строки. Конструкция цикла For ... Next уже была рассмотрена. Но здесь необходим оператор цикла другого вида: Count = 1 While EX.ActiveSheet.Cells(Count, 1).Value "" Count = Count + 1 Wend Цикл While ... Wend выполняется до тех пор, пока помещенное в строку While логическое выражение возвращает значение True (Истина). То есть, пока выражение EX.ActiveSheet.Cells(Count, 1).Value остается не равным пустой строке (""). Это означает, что переменная Count, начав свой отсчет с 1, будет увеличиваться на 1 до тех пор, пока в первом столбце текущего листа не встретится пустая ячейка. После этого можно использовать значение Count во всех тех местах, где необходимо указать номер строки, в которую производится запись — при каждом выполнении макроса это будет номер очередной свободной строки.  см. также в приложении раздел “Цикл While … Wend”. Âòîðîå. Следующая проблема, которую необходимо решить, связана с выбором рабочего листа. В подготовленной книге Excel есть четыре листа, на которых макрос, согласно замыслу, должен помещать сведения о документе. В предыдущем примере обращение к рабочему листу было, как к ActiveSheet — какой бы лист ни был активным, с ним и будем работать. Активным после открытия книги будет тот лист, на котором она была закрыта. Пусть так все и остается, но только до первого обращения к активному листу нам следует сделать активным нужный нам лист книги. Немного упрощая задачу на первом этапе ее решения, предположим, что документы типа “договор”, “акт” и т.д. начинаются с соответствующих слов, то есть, “Договор”, “Акт” и т.д. Предположим также, что слова эти всегда написаны с прописной буквы и все остальные буквы слова — строчные. Нам требуется теперь проанализировать первое слово документа и принять решение, на какой лист его поместить. Если определить принадлежность документа не удалось, его следует отнести к “прочим” документам. С этого и начнем:

EX.Worksheets("Прочее").Activate Теперь, если не будет принято никакого решения, активным останется лист “Прочее” и весь последующий код, где упоминается объект ActiveSheet, будет относиться именно к этому листу. Àíàëèç ïåðâîãî ñëîâà Word-äîêóìåíòà Òðåòüå. Далее следует проанализировать первое слово документа, и, если оно совпадет с “Договор”, “Акт” или “Доверенность”, сделать активным соответствующий лист. Для решения этой задачи нам понадобится конструкция Select Case. Select Case ActiveDocument.Words(1) Case "Договор ": EX.Worksheets("Договор").Activate

82 Глава 3. Поверка гармонии Word алгеброй Excel Case "Акт ": EX.Worksheets("Акт").Activate Case "Доверенность ": EX.Worksheets("Доверенность").Activate End Select Здесь строка Select Case содержит выражение-переключатель, а в каждой из строк Case до двоеточия указана константа, с которой может совпасть значение переключателя, а после двоеточия — оператор, который необходимо в этом случае выполнить.  см. также в приложении раздел “Конструкция Select Case … End Select”.

Обратите внимание на пробелы в конце слов “Договор “, “Акт “ и “Доверенность “. В именах рабочих листов этих пробелов нет, но при анализе строки, возвращаемой семейством Words активного документа, они необходимы. С точки зрения объектной модели Word слово в большинстве случаев — это слово плюс пробел после него. Теперь документ, начинающийся, например, со слова “Доверенность”, будет зарегистрирован на листе “Доверенность”. Чтобы убедиться в этом, следует ввести новую редакцию текста макроса — см. листинг 3.2. ËÈÑÒÈÍÃ 3.2

Sub FileSave() ActiveDocument.Save Dim EX As Excel.Workbook Dim Count As Integer Set EX = GetObject("C:\DocTable.xls") EX.Worksheets("Прочее").Activate Select Case ActiveDocument.Words(1) Case "Договор ": EX.Worksheets("Договор").Activate Case "Акт ": EX.Worksheets("Акт").Activate Case "Доверенность ": EX.Worksheets("Доверенность").Activate End Select Count = 1 While EX.ActiveSheet.Cells(Count, 1).Value "" Count = Count + 1 Wend With EX.ActiveSheet .Cells(Count, 3).Value = _ ActiveDocument.Range(0, 200).Text .Cells(Count, 2).Value = _ ActiveDocument.Paragraphs(1).Range.Text .Hyperlinks.Add _ .Cells(Count, 1), ActiveDocument.FullName End With EX.NewWindow EX.Save Set EX = Nothing End Sub

Из документа Word в таблицу Excel 83 Откроем теперь в окне Word произвольный документ, начинающийся со слова “Доверенность” (рис. 3.8) и сохраним его любым способом. Затем достаточно заглянуть в рабочую книгу DocTable, чтобы убедиться, что документ зарегистрирован на нужном листе (см. рис. 3.9).

Рис. 3.8. В окне Word открыт документ, начинающийся со слова “Доверенность“

Рис. 3.9. Документ зарегистрирован на листе, имя которого совпадает с первым словом документа ×åòâåðòîå. Созданный макрос еще далек от совершенства, но использовать на практике его уже можно. Прежде, чем попытаться создать более “умную” программу, внесем еще одно небольшое усовершенствование. Выше уже отмечалось, что 200 символов текста будут помещены в ячейку третьего столбца для того, чтобы можно было искать документы по вхождению строки. Например, если нас интересует договор с определенной фирмой или доверенность на имя определенного человека или просто документ за каким-то номером, то можно найти нужную строку в таблице при помощи функций поиска Excel, и открыть посредством гиперссылки требуемый документ. Но вот вопрос, сколько символов текста из документа помещать в таблицу? Если системные ресурсы не накладывают сколько-нибудь существенных ограничений, то ничто не мешает помещать в ячейку весь текст документа. Однако существуют ограничения в самой рабочей книге Excel. В версиях MS Office младше 97 в ячейку листа можно поместить не более 255 символов. Начиная с Office 97 это значение увеличено до более, чем 32 тысяч символов. Но все равно может встретиться документ, содержащий столько символов, сколько не поместится в ячейку таблицы. Кроме того, автоматическое копирование в таблицу больших текстов (а их тоже иногда приходится сохранять в окне Word) может привести к неоправданному расходованию ресурсов. С другой стороны, если задаться числом символов, допустим 1000, то может встретиться документ, содержащий менее, чем 1000 символов текста и тогда при выполнении макроса возникнет ошибка выполнения. Чтобы избежать подобных осложнений, можно сделать число сохраняемых символов переменным. В разделе объявления переменных (в начале исходного текста) опишем еще одну, целочисленную переменную, значение которой будет определять число сохраняемых символов текста:

Dim MaxChars As Integer Затем присвоим ей одно из двух значений:

84 Глава 3. Поверка гармонии Word алгеброй Excel If ActiveDocument.Characters.Count < 1000 Then MaxChars = ActiveDocument.Characters.Count Else MaxChars = 1000 End If Если документ содержит менее 1000 символов, то MaxChars будет равняться числу символов в документе — именно эту величину возвращает свойство Count семейства Characters активного документа ActiveDocument. В противном случае MaxChars = 1000. Далее остается отредактировать фрагмент кода, в котором задается число копируемых символов: With EX.ActiveSheet .Cells(Count, 3).Value = _ ActiveDocument.Range(0, MaxChars).Text ... End With

Ìàêðîñ-àðõèâàðèóñ àíàëèçèðóåò ñîäåðæèìîå Wordäîêóìåíòà Если сделать макрос “умнее”, то, возможно, не потребуется хранить в таблице фрагменты текста документов. Если бы можно было помещать в столбец “Описание” все ключевые реквизиты документа, то найти нужный документ не составляло бы труда даже при помощи простого просмотра таблицы, не говоря уже о функциях поиска Excel.

Ïîñòàíîâêà çàäà÷è Разумеется, если речь идет об абсолютно произвольном документе, то задача его анализа не имеет 100%-го решения, по крайней мере, средствами VBA. Возможно, впрочем, что даже технологии искусственного интеллекта здесь не помогли бы. Но не полный семантический анализ не требуется — все, что хотелось бы, это каким-то образом найти в тексте ключевые реквизиты — такие, как даты, номера, имена людей и названия организаций. Тут же, попутно, неплохо было бы распознать вид документа, чтобы поместить его на нужный лист рабочей книги. Метод распознавания по первому слову, использованный в предыдущем примере, конечно, слишком примитивен. Чтобы реализовать простейший анализ документа, необходимо “просмотреть” его текст слово за словом. Это нетрудно сделать — в свойстве Words документа содержится семейство всех его слов, к которым можно обращаться по номерам. Например, выражение ActiveDocument.Words(1) вернет первое слово текущего документа. А в свойстве Count этого же семейства содержится количество слов в документе. Понятно теперь, как можно при помощи цикла For “просмотреть” все слова документа. Конечно, переменная цикла N должна быть предварительно объявлена: For N = 1 To ActiveDocument.Words.Count ... ActiveDocument.Words(N) ... Next N Обращаясь внутри этого цикла к членам семейства Words по индексу N, можно получить доступ последовательно ко всем словам документа. Под словом в документе Word понимается группа символов, отделенная пробелом или знаком препинания, причем знак препинания является самостоятельным словом, а пробел относится к идущему перед ним слову.

Из документа Word в таблицу Excel 85 Но есть ли смысл в просмотре всех слов документа? Скорее всего, все важнейшие реквизиты сосредоточены в его начале. Кроме того, если макросу вдруг встретится документ большого размера, просмотр всех слов может стать довольно длительной операцией. Поэтому остановимся на просмотре первых, например, 100 слов документа. Для случая, когда документ состоит менее, чем из 100 слов, используем переменную WordCount: WordCount = ActiveDocument.Words.Count If WordCount > 100 Then WordCount = 100 For N = 1 To WordCount ... ActiveDocument.Words(N) ... Next N Теперь, если в документе более ста слов, анализироваться будут первые 100, а в противном случае — все слова документа.

Ðåàëèçàöèÿ ìåõàíèçìà àíàëèçà ñîäåðæèìîãî Word-äîêóìåíòà В чем же будет заключаться анализ и каковы должны быть его результаты? Результатом будет строка описания документа, в которую должны быть включены важнейшие, идентифицирующие документ слова. Поместив затем эту строку в ячейку Excel-таблицы, можно получить средство ориентации в документной базе данных. Будем “собирать” строку описания в переменной S, которую необходимо объявить типа String: Dim S As String  см. в приложении подраздел “String” в разделе “Типы данных”.

Мы можем добавлять к ней выбранные слова при помощи оператора присваивания: S = S + ... В результате строка S “накопит” в себе описание документа.  см. также в приложении раздел “Операции со строками”.

Поскольку, с одной стороны, первое слово документа все же неплохо его характеризует, а с другой — нам необходимо начинать просмотр слов не с первого, а со второго (по причинам, которые станут понятными позднее), следует без всяких условий поместить в S первое слово документа: S = ActiveDocument.Words(1) Казалось бы, на тот маловероятный, но все же возможный случай, когда сохраняемый документ не содержит ни одного слова, эту операцию следует обусловить значением переменной WordCount, иначе может возникнуть ошибка выполнения: If WordCount > 1 Then S = S + ActiveDocument.Words(1) На самом деле, по меньшей мере, одно слово содержится даже в пустом документе — непечатаемый символ абзаца, который с точки зрения объектной модели Word также является словом. Наконец, чтобы внутри оператора цикла For не упоминать каждый раз объект ActiveDocument, добавим конструкцию With…End With: WordCount = ActiveDocument.Words.Count

86 Глава 3. Поверка гармонии Word алгеброй Excel

If WordCount > 100 Then WordCount = 100 S = ActiveDocument.Words(1) With ActiveDocument For N = 2 To WordCount ... .Words(N) ... Next N End With Теперь все слова внутри цикла For, начинающиеся с точки (например, .Words(N)), будут подразумевать отношение к объекту ActiveDocument, то есть к активному документу. Итак, все готово для проведения собственно анализа. Остается наполнить “скелет” конструкции анализатора программным кодом, который выбирал бы среди встречающихся слов наиболее важные с тем, чтобы запомнить их в строке описания документа. Как и по какому принципу выбирать слова? Тут возможны различные подходы. Какой из них выбрать — зависит в значительной степени от предметной области, к которой относятся рассматриваемые документы. Постараемся продемонстрировать общие принципы, на основе которых (как надеется автор) каждый сможет сформировать собственный анализатор. Итак, существует два основных класса возможностей. Можно выбирать слова, основываясь на их значении, или же ориентироваться на признаки форматирования слова.

Àíàëèç çíà÷åíèé ñëîâ â äîêóìåíòå Word Проиллюстрируем вначале первый подход. В предыдущем варианте макроса при помощи конструкции Select Case уже сравнивалось первое слово документа с несколькими строковыми значениями (см. раздел “Анализ первого слова Word-документа”). Тогда, было специально оговорено, что сравниваемое слово должно состоять из строчных букв, а начинаться с прописной буквы. Ïîñòàíîâêà çàäà÷è Конечно, в реальных документах слова могут быть написаны абсолютно произвольным образом. Чтобы решить эту проблему, нам потребуется помощь функции UCase. Получив строку, эта функция возвращает ту же строку, где все буквы — прописные. Если сравнивать возвращенное функцией UCase значение со строкой из прописных букв, то нет разницы, из каких букв состояло исходное слово — сравнение в любом случае будет корректным. Иными словами, выражение UCase(ActiveDocument.Words(N)) вернет слово “ДОГОВОР” вне зависимости от того, из каких букв состояло N-е слово в текущем документе: “Договор”, “ДОГОВОР” или “дОгоВОр”.  см. также в приложении раздел “Преобразования регистра (функции LCase, UCase)”.

Ранее уже упоминался тот факт, что, если слово в документе сопровождается пробелом, семейство Words возвращает слово вместе с пробелом. Если же пробела после слова нет, то, естественно, Words(N) вернет N-е слово без пробела. Чтобы избежать разночтений в этом вопросе, применим функцию Trim. Эта функция просто удаляет пробелы перед и после переданной ей строки. Таким образом, если сравнивать значение выражения UCase(Trim(ActiveDocument.Words(N))) со строковым значением, например, “ДОГОВОР”, то никакие пробелы или различия между строковыми и прописными буквами не помешают.

Из документа Word в таблицу Excel 87  см. также в приложении раздел “Функции удаления пробелов (LTrim, RTrim, Trim)”.

Àíàëèç ñëîâà Word-äîêóìåíòà Итак, попробуем проанализировать очередное слово посредством конструкции Select Case, основываясь на значении и, кстати, заодно более корректным способом распознаем вид документа с тем, чтобы зарегистрировать его на нужном листе рабочей книги Excel: Select Case UCase(Trim(ActiveDocument.Words(N))) Case "№", "#", "N": _ S = S + "№" + ActiveDocument.Words(N + 1) Case "ДОГОВОР": _ EX.Worksheets("Договор").Activate Case "ДОВЕРЕННОСТЬ": _ EX.Worksheets("Доверенность").Activate Case "АКТ": _ EX.Worksheets("Акт").Activate End Select ÏÐÈÌÅ×ÀÍÈÅ

Напомним, что сочетание пробела со знаком подчеркивания ( _) означает перенос на следующую строку. Редактор Visual Basic “понимает” разделенные такими символами строки как одну строку. Необходимость переноса возникает при размещении текста на книжной странице. Необходимо учитывать при этом, что редактор Visual Basic позволяет ввести сколь угодно длинную строку, и на самом деле необходимости в таких переносах нет. Строка Case "№", "#", "N": _ S = S + "№" + ActiveDocument.Words(N + 1) иллюстрирует возможный способ анализа. Если очередное (N-е) слово представляет собой один из “номерных” символов ("№", "#" или "N"), то следующее после него (то есть, N+1-е слово) поместим в строку описания — поскольку это, скорее всего, и есть номер документа. Если же очередным словом оказывается, например, “ДОГОВОР”, то нужно просто сделать активным соответствующий лист рабочей книги. Заметим, в данном операторе остается возможность для ошибки выполнения, если в документе меньше 100 слов и последнее, N-е слово представляет собой “номерной” символ, то произойдет обращение к несуществующему N+1-му слову! ÏÐÈÌÅ×ÀÍÈÅ

Анализ, основанный на значении слова, может принимать разнообразные формы. Можно обращаться к последующему (N+1) или предыдущему (N-1) словам. Чтобы извлечь из документа и поместить в строку-накопитель не единственное слово, а последовательность слов, необходимо использовать свойства объекта Range — текстового диапазона. Например, для помещения в строку S непрерывного текстового фрагмента можно использовать оператор вида: S=S + ActiveDocument.Range(Начальный_символ, Конечный_символ) В скобках необходимо задать каким-нибудь образом ссылки на начало и конец текстового диапазона. Например, можно просто явным образом указывать конкретный символ конкретного слова — ActiveDocument.Words(N).Characters(M). Если нужно включать слова целиком, то следует использовать свойства Start и End. Эти свойства представляют собой ссылки на первый и последний символы слова. Например, выражение ActiveDocument.Range(ActiveDocument.Words(N).Start, _

88 Глава 3. Поверка гармонии Word алгеброй Excel ActiveDocument.Words(N + 10).End) вернет текстовый фрагмент, заключающий в себе 11 слов, начиная с N-го. При расчете подобных ссылок следует помнить о том факте, что знаки препинания и непечатаемый символ абзаца считаются отдельными словами. Конструкция оператора Case допускает проверку множества вариантов для каждой ветви выбора. Вот как можно было бы, например, извлечь из текста дату в формате “ДД месяц ГГГГ”: With ActiveDocument ... Select Case UCase(Trim(.Words(N))) Case "ЯНВАРЯ", "ФЕВРАЛЯ", "МАРТА", "АПРЕЛЯ", _ "МАЯ", "ИЮНЯ", "ИЮЛЯ", "АВГУСТА", _ "СЕНТЯБРЯ", "ОКТЯБРЯ", "НОЯБРЯ", _ "ДЕКАБРЯ": S = S + _ ActiveDocument.Range(.Words(N - 1).Start, _ .Words(N + 1).End) ... End Select ... End With Если, например, очередным (N-м) словом окажется слово “октября”, то в строку S попадут три слова — слово перед словом “октября”, само слово “октября” и слово после “октября”.

Ïîñèìâîëüíûé àíàëèç ñëîâ â äîêóìåíòå Word Ничто не мешает нам одновременно применить другой подход к анализу документа в пределах все того же цикла For, пока выражение .Words(N) все еще содержит очередное слово документа. Ïîñòàíîâêà çàäà÷è Можно было бы, например, выбирать слова, выделенные жирным шрифтом или курсивом. Для этого необходимо обратиться к свойствам отдельного символа слова. Символы каждого слова в документе представлены его свойством Characters. Например, первый символ N-го слова заключен в объекте ActiveDocument.Words(N).Characters(1). За признаки начертания полужирный и курсив отвечают соответственно свойства символа Bold и Italic — эти свойства возвращают логические значения True (Истина) или False (Ложь). Поэтому в условном операторе If нет необходимости с чем-либо сравнивать эти значения. Если символ, например, выделен полужирным шрифтом, то его свойство Bold вернет значение True. Ïðèìåíåíèå ëîãè÷åñêîé îïåðàöèè Or (ëîãè÷åñêîå ÈËÈ) If .Words(N).Characters(1).Bold _ Or _ .Words(N).Characters(1).Italic _ Then S = S + .Words(N) End If Здесь условная конструкция немного усложнена по сравнению с теми, которые были рассмотрены ранее. Строка If… превратилась здесь в три строки, причем первая и третья содержат два независимых условия, объединенных логической операцией Or (логическое ИЛИ). Если первый

Из документа Word в таблицу Excel 89 символ анализируемого слова выделен полужирным ИЛИ курсивом, то все слово будет добавлено в строку описания.  см. также в приложении раздел “Логические операции”.

Действуя по аналогичному принципу, можно выбрать слова, начинающиеся с прописной буквы — за исключением тех, что являются первым словом предложения. Таким способом можно выделить в тексте имена людей и названия организаций, а также аббревиатуры. Ïðèìåíåíèå ëîãè÷åñêîé îïåðàöèè And (ëîãè÷åñêîå È) Чтобы определить, является ли символ прописной буквой, достаточно прочитать значение его свойства Case. Если в нем содержится константа wdUpperCase, значит, символ является прописной буквой. If .Words(N).Characters(1).Case = wdUpperCase _ And _ .Words(N - 1) "." _ And _ .Words(N - 1) Chr(13) _ Then S = S + .Words(N) End If Здесь в одном операторе If объединены три логических условия, причем объединяет их на этот раз логическая операция And (логическое И). Второе и третье условия касаются предыдущего, то есть N-1-го слова — это слово должно НЕ являться знаком препинания “точка” И (то есть, одновременно с этим) НЕ являться непечатаемым символом конца абзаца Chr(13), чтобы все выражение вернуло логическое значение Истина. Только при выполнении всех трех условий слово будет добавлено в строку описания. Благодаря этому большинство слов в начале предложений будет проигнорировано, несмотря на первую прописную букву и в строку описания попадут, в основном, имена собственные. Кстати сказать, именно ради этого оператора начинался отсчет со второго, а не с первого слова — в противном случае пришлось бы усложнить эту и без того сложную конструкцию еще одним условным оператором (условием).  см. также в приложении раздел “Логические операции”.

Наконец, хорошо бы “выловить” в тексте все слова, начинающиеся с цифры, — как правило, с цифры начинаются такие реквизиты, как номера, суммы и даты, которые очень хорошо характеризуют документ и зачастую однозначно его идентифицируют. If .Words(N).Characters(1) >= "0" _ And _ .Words(N).Characters(1) 100 Then WordCount = 100 S = ActiveDocument.Words(1) With ActiveDocument For N = 2 To WordCount Select Case UCase(Trim(.Words(N))) Case "№", "#", "N": _ S = S + "№" + .Words(N + 1) Case "ДОГОВОР": _ EX.Worksheets("Договор").Activate Case "ДОВЕРЕННОСТЬ": _ EX.Worksheets("Доверенность").Activate Case "АКТ": _ EX.Worksheets("Акт").Activate End Select If .Words(N).Characters(1).Case = wdUpperCase _ And _ .Words(N - 1) "." _ And _ .Words(N - 1) Chr(13) _ Then S = S + .Words(N)

Из документа Word в таблицу Excel 91 End If If .Words(N).Characters(1) >= "0" _ And _ .Words(N).Characters(1) "1" _ Or _ Mid(S, 2, 1) = "0" Then ... End If Внутри нее можно добавить к строке суммы прописью десятки и единицы обычным образом. Вначале: Select Case Mid(S, 2, 1) Case "2": SUM = SUM + "двадцать " ... Case "9": SUM = SUM + "девяносто " End Select А затем: Select Case Mid(S, 3, 1) Case "1": SUM = SUM + "одна " ... Case "9": SUM = SUM + "девять " End Select Далее, перед переходом от тысяч к “простым” сотням, десяткам и единицам, нас подстерегает маленькая проблема. Нельзя просто добавить к строке SUM слово “тысяч” и двигаться дальше. Возможны случаи как “тысяч” (например, 11, 100, 155), так и “тысяча” (например, 1, 21, 101 ) и даже “тысячи” (например, 2, 34, 503). Все эти варианты необходимо правильно распознать и добавить к строке SUM верное слово. Начнем с того, что тысяч в числе суммы вообще может не быть и в этом случае соответствующее слово добавлять нельзя: If SUM "" Then ... If Затем, разделим варианты на “от 10 до 19” и “все остальное”: If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч " Else ... End If Для “всего остального” будем различать три случая — одна тысяча, две/три/четыре тысячи и опять же “все остальное”: Select Case Mid(S, 3, 1) Case "1": SUM = SUM + "тысяча " Case "2", "3", "4": SUM = SUM + "тысячи " Case Else: SUM = SUM + "тысяч " End Select

Сумма прописью 107 Строка Case Else выполняется в случае, когда ни одна из строк Case не выполнена. В результате получим весьма разветвленную конструкцию: If SUM "" Then If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч " Else Select Case Mid(S, 3, 1) Case "1": SUM = SUM + "тысяча " Case "2", "3", "4": SUM = SUM + "тысячи " Case Else: SUM = SUM + "тысяч " End Select End If End If  см. также в приложении раздел “Конструкция Select Case … End Select”.

Следующую тройку разрядов − 4-й, 5-й и 6-й, то есть сотни, десятки и единицы, можно обработать почти точно таким же способом. Единственная особенность, из-за которой их приходится обрабатывать отдельно, заключается в разном роде слов “тысяча” и “рубль”. Придется использовать “один” вместо “одна” и “два” вместо “две”. К полученной в результате строке SUM необходимо еще добавить окончание “руб.” и число копеек: SUM = SUM + " руб. " + Mid(S, 8, 2) + " коп." Функция Mid извлекает два последних символа из строки S, в предположении, что это должны быть цифры, соответствующие числу копеек в сумме (по бухгалтерским правилам, в сумме прописью число копеек пишется цифрами). О том, чтобы это действительно было число копеек, “позаботились” функция Format и денежный числовой тип Currency. Остается сделать еще один реверанс бухгалтерским правилам — сумма прописью должна начинаться с прописной буквы. Поэтому, надо не просто присвоить функции в качестве возвращаемого значения строку SUM, а немного преобразовать ее: Сумма_прописью = UCase(Left(SUM, 1)) + Right(SUM, Len(SUM) - 1) Функция UCase уже была рассмотрена (см. в гл. 3 раздел “Анализ значений слов в документе Word”) — она преобразует произвольную строку в строку с только прописными буквами.  см. также в приложении раздел “Преобразования регистра (функции LCase, UCase)”.

Функции Left и Right возвращают заданное число символов из строки-параметра, соответственно, слева и справа.  см. также в приложении раздел “Выделение подстроки”.

Функция Len возвращает число, равное длине строки-параметра.  см. также в приложении раздел “Форматирование строки”.

Таким образом, оператор соберет строковое значение из первого символа, преобразованного в верхний регистр и всех остальных символов кроме первого, никак не преобразованных. В итоге можно записать исходный текст функции Сумма_прописью (листинг 4.1). ËÈÑÒÈÍÃ 4.1

Public Function Сумма_прописью(N As Currency) As String Dim S, SUM As String S = Format(N, "000000.00")

108 Глава 4. Практикум программирования на VBA для Excel и Word

SUM = "" Select Case Case Case Case Case Case Case Case Case Case Case End Select

Mid(S, 1, 1) "0": "1": SUM = SUM "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM "9": SUM = SUM

+ + + + + + + + +

"сто " "двести " "триста " "четыреста " "пятьсот " "шестьсот " "семьсот " "восемьсот " "девятьсот "

If Mid(S, 2, 1) > "1" _ Or _ Mid(S, 2, 1) = "0" Then Select Case Case Case Case Case Case Case Case Case End Select

Mid(S, 2, 1) "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM "9": SUM = SUM

+ + + + + + + +

"двадцать " "тридцать " "сорок " "пятьдесят " "шестьдесят " "семьдесят " "восемьдесят " "девяносто "

Select Case Case Case Case Case Case Case Case Case Case End Select

Mid(S, 3, 1) "1": SUM = SUM "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM "9": SUM = SUM

+ + + + + + + + +

"одна " "две " "три " "четыре " "пять " "шесть " "семь " "восемь " "девять "

If Mid(S, 2, 1) = "1" Then Select Case Mid(S, 3, 1) Case "0": SUM = SUM Case "1": SUM = SUM Case "2": SUM = SUM Case "3": SUM = SUM Case "4": SUM = SUM Case "5": SUM = SUM Case "6": SUM = SUM Case "7": SUM = SUM

+ + + + + + + +

"десять " "одиннадцать " "двенадцать " "тринадцать " "четырнадцать " "пятнадцать " "шестнадцать " "семнадцать "

End If

Сумма прописью 109 Case "8": SUM = SUM + "восемнадцать " Case "9": SUM = SUM + "девятнадцать " End Select End If If SUM "" Then If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч " Else Select Case Mid(S, 3, 1) Case "1": SUM = SUM + "тысяча " Case "2", "3", "4": SUM = SUM + "тысячи " Case Else: SUM = SUM + "тысяч " End Select End If End If Select Case Case Case Case Case Case Case Case Case Case Case End Select

Mid(S, 4, 1) "0": "1": SUM = SUM "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM "9": SUM = SUM

+ + + + + + + + +

"сто " "двести " "триста " "четыреста " "пятьсот " "шестьсот " "семьсот " "восемьсот " "девятьсот "

If Mid(S, 5, 1) > "1" _ Or _ Mid(S, 5, 1) = "0" Then Select Case Case Case Case Case Case Case Case Case End Select

Mid(S, 5, 1) "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM "9": SUM = SUM

+ + + + + + + +

"двадцать " "тридцать " "сорок " "пятьдесят " "шестьдесят " "семьдесят " "восемьдесят " "девяносто "

Select Case Case Case Case Case Case Case Case Case

Mid(S, 6, 1) "1": SUM = SUM "2": SUM = SUM "3": SUM = SUM "4": SUM = SUM "5": SUM = SUM "6": SUM = SUM "7": SUM = SUM "8": SUM = SUM

+ + + + + + + +

"один " "два " "три " "четыре " "пять " "шесть " "семь " "восемь "

110 Глава 4. Практикум программирования на VBA для Excel и Word Case "9": SUM = SUM + "девять " End Select End If If Mid(S, 5, 1) = "1" Then Select Case Mid(S, 6, 1) Case "0": SUM = SUM Case "1": SUM = SUM Case "2": SUM = SUM Case "3": SUM = SUM Case "4": SUM = SUM Case "5": SUM = SUM Case "6": SUM = SUM Case "7": SUM = SUM Case "8": SUM = SUM Case "9": SUM = SUM End Select End If

+ + + + + + + + + +

"десять " "одиннадцать " "двенадцать " "тринадцать " "четырнадцать " "пятнадцать " "шестнадцать " "семнадцать " "восемнадцать " "девятнадцать "

SUM = SUM + " руб. " + Mid(S, 8, 2) + " коп." Сумма_прописью = UCase(Left(SUM, 1)) + Right(SUM, Len(SUM) - 1) End Function Функция готова! Готова к чему? Можно использовать ее теперь в любом макросе, где она потребуется.

Èñïîëüçîâàíèå ôóíêöèè Ñóììà_ïðîïèñüþ() Предположим, требуется автоматически формировать столбец “сумма прописью” по значениям столбца “сумма”, в который эти числовые значения либо вводятся пользователем, либо отображаются в него посредством связей Automation. Чтобы решить эту задачу, будем обрабатывать событие Change. При любом изменении значения в одной из ячеек листа будет сгенерировано это событие. Ссылка на ячейку при этом передается в процедуру обработки события посредством параметра Target.

Ñîçäàíèå ïðîöåäóðû-îáðàáîò÷èêà ñîáûòèÿ Change äëÿ ëèñòà ðàáî÷åé êíèãè Excel Читатель уже без труда создаст процедуру-обработчик события Change для того листа рабочей книги, где такой обработчик требуется. Вот, какой исходный текст следует поместить в эту процедуру (листинг 4.2). ËÈÑÒÈÍà 4.2

Private Sub Worksheet_Change(ByVal Target As Range) On Error Resume Next If Target.Column = 1 Then Target.Offset(0, 1).Value = Сумма_прописью(Target.Value) End If End Sub

Сумма прописью 111 Параметр Target указывает на изменившуюся ячейку. А свойство Offset(0, 1) этой ячейки указывает на ячейку справа от нее. (Нетрудно догадаться, что, скажем, выражение Offset(1, 2) указывало бы на ячейку, находящуюся одной строкой ниже и двумя столбцами правее). И вот, если речь идет о ячейке первого столбца (Column = 1), то в ячейку справа поместим значение, возвращенное функцией Сумма_прописью. Разумеется, предполагается, что столбцы, о которых идет речь, отформатированы надлежащим образом — для ввода денежных и текстовых значений соответственно. Теперь можно проверить работоспособность макроса путем ввода различных числовых значений в первый столбец. Вернемся в окно MS Excel и введем значения в первый столбец того листа, для которого был создан обработчик события Change. Результат изображен на рис. 4.2.

Рис. 4.2. Строки с суммой прописью формируются автоматически в столбце B

Çàùèòà îò âîçíèêíîâåíèÿ êàñêàäíûõ ñîáûòèé ÏÐÈÌÅ×ÀÍÈÅ

Остается прояснить маленький вопрос — зачем в начале процедуры использована строка On Error Resume Next, защищающая макрос от прерываний при возникновении ошибок. Какие ошибки могут тут возникнуть? И еще: зачем проверять номер столбца? Дело в том, что событие Change возникает практически при любом изменении значения ячейки. И если просто прочитать значение некоторой ячейки и записать возвращенное функцией значение в ячейку справа, то возникнет так называемое каскадное событие. Обработка события Change для первой ячейки еще не завершилось, но уже возникает событие Change для ячейки справа (ведь в нее записано новое значение!), которое, в свою очередь, вызовет событие Change для следующей ячейки справа и так далее. Факт обработки события является в данном случае причиной возникновения такого же события (это, кстати сказать, классическая причина возникновения каскадных событий). В результате возникнет “бесконечная” последовательность событий, которая приведет к аварийному прерыванию процедуры. Такая картина событий могла быть, если бы функция возвращала значение такого же типа, что и прочитанное в первой ячейке, или, если бы не был указан явным образом тип параметра и тип возвращаемого значения. В данном случае уже самое первое излишнее событие Change приведет к ошибке, поскольку функции Сумма_прописью будет передано строковое значение из второго столбца (а ожидает она, как указано, числовое денежное значение типа Currency). Благодаря этому обстоятельству, а также благодаря строке On Error Resume

112 Глава 4. Практикум программирования на VBA для Excel и Word Next цепочка каскадного события в нашем случае оборвется в самом начале. Но общий способ “предохранения” от каскадных событий состоит в том, чтобы ограничить сферу действия процедуры достаточно узким диапазоном. Для этого и наложено условие If Target.Column = 1 Then… (если изменившаяся ячейка принадлежит столбцу 1, то). ← см. также в гл. 3 раздел “Управление ошибками выполнения макроса”.

×àñòîòíûé ñëîâàðü Вряд ли эту задачу можно отнести к часто встречающимся проблемам современного офиса. И все же, автоматическое составление частотного словаря не лишено практического смысла в некоторых ситуациях, когда имеет значение именно словарный состав документа. Главная же причина, по которой задача “Частотный словарь” включена в состав данной главы, заключается в том, что для ее решения необходимо будет широко использовать разнообразные возможности языка VBA, и, в то же самое время, достаточно глубоко “заглянуть” в объектную модель документа Word. Словом, решение этой задачи — хорошая практика для человека, намеревающегося работать с документами Word посредством макросов VBA.

Ïîñòàíîâêà çàäà÷è Итак, в чем же, собственно, заключается задача? Необходимо написать макрос, который составлял бы частотный словарь документа Word. Словарь должен состоять из заданного числа слов, наиболее часто встречающихся в документе. Например, 10 или 500 наиболее употребительных в данном документе слов. Конечно, отобранные слова необходимо упорядочить в порядке убывания их частоты — это обычный принцип построения частотных словарей. В какой форме должен быть создан словарь? Разумеется, в форме нового документа Word — работая в среде Word было бы странно избрать какой-то иной путь. Каким образом будет запускаться будущий макрос? На этот раз самым, что ни на есть, обыкновенным способом. Это будет просто макрос с именем “Частотный_словарь”, который можно будет запустить при помощи команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû. Вначале необходимо создать его, введя в поле Èìÿ строку “Частотный_словарь” и щелкнув на кнопке Ñîçäàòü. А затем, после того, как исходный текст макроса будет введен, его можно будет выполнить при помощи той же команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû — достаточно выделить в списке имя “Частотный_словарь” и щелкнуть на кнопке Âûïîëíèòü.

Ðàçðàáàòûâàåì èñõîäíûé òåêñò ìàêðîñà Зададимся вначале параметрами словаря. Дело в том, что для подсчета числа вхождений слова в текст документа нам потребуется где-то хранить все встреченные отличающиеся друг от друга слова. Неизбежно поэтому использование массива строковых значений, то есть специальной переменной, к однородным элементам которой можно обращаться по их порядковым номерам.

Èñïîëüçîâàíèå ìàññèâîâ Размерность буферного массива имеет большое значение. Не известно, сколько разных слов может содержаться в документе, поэтому идеальный массив для подсчета слов должен бы был являться бесконечным. Увы, это невозможно, и нам придется пойти на компромисс. Допустим, что самый большой документ, который может встретиться, состоит из 5000 слов. Тогда вполне разумным выглядит предположение, что предстоит иметь дело с не более чем 3000 разных слов. Пусть массив строк для хранения подсчитываемых слов имеет размерность 3000. Размер словаря можно задать произвольным образом — пусть он состоит из 200 наиболее часто встречающихся слов. Îáúÿâëåíèå êîíñòàíò Эти две величины имеет смысл объявить в качестве констант, тогда их нетрудно будет изменить — достаточно исправить значение в объявлении константы и соответствующая величина изменится повсюду в программе:

Частотный словарь 113 Const MaxWords = 3000 Const WordsInDictionary = 200  см. также в приложении раздел “Константы”.

Затем нужно объявить два массива: в одном из них (Words) будут храниться сами подчитываемые слова, а второй (WordFrq) будет содержать соответствующие этим словам частоты: Dim Words(0 To MaxWords) As String Dim WordFrq(0 To MaxWords) As Integer В результате будут созданы два массива размером 3001 элемент каждый (3001, потому что отсчет начат с 0): один для хранения строковых значений типа String, и второй — для целых чисел типа Integer. Кроме того, потребуются другие переменные различных типов. Каждая из них будем упоминаться по мере необходимости.  см. также в приложении раздел “Массивы”.

Прежде всего, инициализируем массивы: при очередном запуске макроса каждый элемент массива Words должен содержать пустую строку, а каждый элемент WordFrq — число 0. For I = 0 To MaxWords Words(I) = "" WordFrq(I) = 0 Next I Теперь необходимо перебрать все слова документа и выполнить соответствующие подсчеты. Как просмотреть документ слово за словом, уже было рассмотрено. ← см. в гл. 3 раздел “Макрос-архивариус анализирует содержимое Word-документа”.

Это без труда можно осуществить, используя обычный цикл For, и обращаясь к элементам семейства ActiveDocument.Words по номерам. Но, вообще говоря, в языке программирования Visual Basic для приложений имеется специальная конструкция For Each, предназначенная для перебора всех элементов какого-либо семейства. Если объявить переменную типа Object (назовем ее MyWord), то при помощи приведенного ниже оператора можно последовательно получить в эту переменную все слова документа: For Each MyWord In ActiveDocument.Words ... Next MyWord Внутри этой конструкции при каждом проходе цикла переменная MyWord представляет текстовый диапазон (Range-объект), заключающий в себе очередное слово.  см. также в приложении разделы “Объектные переменные” и “Цикл For Each … Next”.

Как уже отмечалось, под словом MS Word и VBA могут понимать как слово в обычном смысле, так и знак препинания или непечатаемый символ абзаца. Чтобы отсеять все эти “слова”, а также, чтобы не включать в рассмотрение слова из одной или двух букв, наложим условие: “только для слов, состоящих из более чем двух символов” (напомним, строковая функция Trim удаляет пробелы перед и после слова, а функция Len возвращает длину строки, переданной ей в параметре): For Each MyWord In ActiveDocument.Words If Len(Trim(MyWord)) > 2 Then ... End If Next MyWord

114 Глава 4. Практикум программирования на VBA для Excel и Word  см. также в приложении разделы “Функции удаления пробелов (LTrim, RTrim, Trim)” и “Форматирование строки”.

Теперь можно считать, что “скелет” конструкции макроса готов. Остается создать программный код, который непосредственно подсчитывает частоту слов в документе. Для этого потребуются две переменные. Целочисленная переменная WordCount будет играть роль “счетчика слов”, указывающего на первую свободную позицию в массиве Words (понятно, что вначале массив пуст и переменная WordCount указывает на первый его элемент). Затем, по мере заполнения массива встреченными словами, WordCount будет отодвигать границу занятой части массива все дальше и дальше. При рассмотрении каждого очередного слова в документе предполагается просматривать массив Words на предмет совпадений с текущим словом, и, если совпадения не обнаружится, добавим слово в массив в позицию WordCount, а саму границу WordCount отодвинем дальше. Если же найдено совпадение, то нужно просто увеличить на единицу значение частоты для данного элемента в параллельном массиве WordFrq. Вторая переменная, FoundFlag, должна играть роль флажка: “Есть совпадение!”, поэтому она должна принадлежать к логическому типу Boolean (переменные этого типа могут принимать только значения True или False — Истина или Ложь).  см. также в приложении раздел “Boolean”.

Итак, в переменной MyWord содержится очередное слово документа. Перед просмотром массива сбросим флажок FoundFlag — совпадений пока не найдено: FoundFlag = False Затем “сканируем” массив Words при помощи переменной цикла I вплоть до значения, содержащегося в счетчике WordCount: For I = 0 To WordCount If UCase(Trim(MyWord)) = Words(I) Then WordFrq(I) = WordFrq(I) + 1 FoundFlag = True End If Next I Если текущее слово документа совпало с одним из слов, зарегистрированных в массиве Words, то соответствующий элемент в массиве WordFrq (то есть частота данного слова) будет увеличен на единицу. Одновременно установим флажок FoundFlag в положение True, сигнализируя о том, что слово встретилось в массиве и регистрировать его не нужно. А вот если слово не найдено (FoundFlag = False, или, что то же самое, Not FoundFlag), то необходимо добавить его в массив Words, записав 1 в соответствующей позиции массива частот и передвинув на 1 вперед счетчик WordCount: If Not FoundFlag Then Words(WordCount) = UCase(Trim(MyWord)) WordFrq(I) = WordFrq(I) + 1 WordCount = WordCount + 1 ... End If Поскольку в этой ветви условной конструкции увеличивается значение переменной WordCount, необходимо здесь же проверить, не достигло ли оно границы массива — если, да, то надо что-то предпринять, поскольку запись в несуществующий элемент массива приведет к ошибке выполнения:

Частотный словарь 115 If WordCount >= MaxWords - 1 Then MsgBox "Все слова не поместились!" GoTo Finish End If Здесь, при помощи оператора перехода GoTo осуществляется “аварийный” выход из цикла For Each MyWord…Next MyWord, поэтому метку Finish поместим за его пределами.  см. также в приложении раздел “Переход по метке”.

После окончания работы цикла массив Words будем содержать все отличающиеся слова документа с указанием их частот в соответствующих позициях массива WordFrq.

Ñîçäàíèå Word-äîêóìåíòîâ è íîâûõ àáçàöåâ â íèõ ïðè ïîìîùè ìåòîäà Add Теперь осталось создать новый документ Word и сформировать в нем частотный словарь. Чтобы каждая позиция словаря располагалась на отдельной строке документа, необходимо для каждой позиции создавать новый абзац. Создание абзаца подобно созданию документа. В обоих случаях следует использовать заранее объявленные объектные переменные соответствующих типов,… Dim MyDocument As Document Dim MyParagraph As Paragraph а затем присвоить им значения при помощи оператора Set и метода Add соответствующего семейства (семейства документов или абзацев): Set MyDocument = Documents.Add Set MyParagraph = MyDocument.Paragraphs.Add Чтобы вставить перед символом вновь созданного абзаца какой-либо текст (в данном случае — заголовок словаря), следует воспользоваться методом InsertBefore: MyParagraph.Range.InsertBefore "Слово,Частота" Теперь остается создать заданное число (то есть, WordsInDictionary) строк словаря. Это было бы простой задачей, если бы массив Words был упорядочен по частотам.

Óïîðÿäî÷èâàíèå ýëåìåíòîâ ìàññèâà ïî óáûâàíèþ Придется объединить создание строк с упорядочиванием массива. Для этого необходимо просто найти в массиве слово с самой большой частотой, вывести его в результирующий документ и вслед за этим исключить из рассмотрения, присвоив ему частоту, равную нулю. После этого можно снова искать слово с наибольшей частотой — это будет второе по частоте слово словаря. И так далее — аналогичные действия должны быть выполнены WordsInDictionary раз. Понятно, что основой конструкции должен быть цикл вида: For M = 1 To WordsInDictionary ... Next M Чтобы найти слово с наибольшей частотой необходимо “просканировать” массив WordFrq и найти самое большое число, сравнивая каждый очередной элемент массива с некоторой переменной. Пусть это будет целочисленная переменная N, которой вначале необходимо присвоить значение 0: N = 0

116 Глава 4. Практикум программирования на VBA для Excel и Word For G = 0 To WordCount If WordFrq(G) > N Then N = WordFrq(G) WordPointer = G End If Next G В результате номер слова с самой большой частотой будет сохранен в переменной WordPointer. Теперь следует сформировать очередную строку словаря: Set MyParagraph = MyDocument.Paragraphs.Add MyParagraph.Range.InsertBefore _ (Words(WordPointer) + "," + Str(WordFrq(WordPointer))) Как и заголовок, каждая строка будет состоять из слова и числа, характеризующего его частоту, разделенные запятой. Наконец, в конце очередного прохода цикла следует обнулить частоту только что обработанного слова и задача оказывается сведенной в точности к предыдущему шагу: требуется найти слово с самой большой частотой … WordFrq(WordPointer) = 0 Новый документ сформирован, и, чтобы сделать его удобочитаемым, можно отформатировать созданные абзацы, преобразовав их в таблицу.

Ïðåîáðàçîâàíèå ñòðîê Word-äîêóìåíòà â òàáëèöó ïðè ïîìîùè ìåòîäà ConvertToTable Подобно команде Òàáëèöà | Ïðåîáðàçîâàòü | Ïðåîáðàçîâàòü â òàáëèöó, метод ConvertToTable применим к любому текстовому диапазону и требует того же набора параметров. Параметр NumColumns задает число столбцов. Константа, присвоенная в качестве значения параметру Format, определяет один из вариантов автоформата таблицы. Наконец, параметр Separator назначает символ-разделитель (вот для чего нужна была запятая между словами в строках словаря!). Итак, получаем следующее выражение: MyDocument.Range.ConvertToTable _ NumColumns:=2, Format:=wdTableFormatList4 _ , Separator:="," ÏÐÈÌÅ×ÀÍÈÅ

Справедливости ради нужно отметить, что такой подход несколько отличается от того, который предлагается компанией Microsoft: по правилам должны использоваться специальные константы вида wdSeparator. Но данная книга предполагает освоение практического освоения программирования на Microsoft VBA и часто предлагает нестандартные, но эффективные подходы для решения офисных задач. По мнению автора предложенный метод в данном примере проще и понятней начинающему пользователю, а главное проверен на практике и работает.  см. подробнее фирменную документацию по Microsoft VBA или раздел справочной системы Microsoft Office, посвященный VBA.

Àâòîôîðìàò òàáëèöû Метод ConvertToTable будет применен ко всему текстовому диапазону документа (MyDocument.Range) сразу, в результате чего из всех имеющихся строк словаря будет создана таблица с применением автоформата “Список4” (именно этому значению соответствует константа

Частотный словарь 117 wdTableFormatList4 — для того чтобы узнать имена констант, соответствующих другим форматам таблицы, необходимо воспользоваться справкой VBA. В конечном итоге исходный текст макроса должен выглядеть следующим образом (листинг 4.3). ËÈÑÒÈÍÃ 4.3

Public Sub Частотный_словарь() Const MaxWords = 3000 Const WordsInDictionary = 200 Dim Words(0 To MaxWords) As String Dim WordFrq(0 To MaxWords) As Integer Dim Dim Dim Dim Dim Dim

MyWord As Object WordCount, WordPointer As Integer FoundFlag As Boolean MyParagraph As Paragraph MyDocument As Document MyTable As Table

Dim I, N, M, G As Integer WordCount = 0 For I = 0 To MaxWords Words(I) = "" WordFrq(I) = 0 Next I For Each MyWord In ActiveWindow.Document.Words If Len(Trim(MyWord)) > 2 Then FoundFlag = False For I = 0 To WordCount If UCase(Trim(MyWord)) = Words(I) Then WordFrq(I) = WordFrq(I) + 1 FoundFlag = True End If Next I If Not FoundFlag Then Words(WordCount) = UCase(Trim(MyWord)) WordFrq(I) = WordFrq(I) + 1 WordCount = WordCount + 1 If WordCount >= MaxWords - 1 Then MsgBox "Все слова не поместились!" GoTo Finish End If

118 Глава 4. Практикум программирования на VBA для Excel и Word

End If End If Next MyWord Finish: Set MyDocument = Documents.Add Set MyParagraph = MyDocument.Paragraphs.Add MyParagraph.Range.InsertBefore "Слово,Частота" For M = 1 To WordsInDictionary N = 0 For G = 0 To WordCount If WordFrq(G) > N Then N = WordFrq(G) WordPointer = G End If Next G Set MyParagraph = MyDocument.Paragraphs.Add MyParagraph.Range.InsertBefore _ (Words(WordPointer) + "," + Str(WordFrq(WordPointer))) WordFrq(WordPointer) = 0 Next M MyDocument.Range.ConvertToTable _ NumColumns:=2, Format:=wdTableFormatList4 _ , Separator:="," MyDocument.Activate End Sub

Сравнительный анализ прайс-листов 119 Рис. 4.3. В окне Word сформирован новый документ, содержащий частотный словарь с заданными параметрами

Èñïîëüçîâàíèå ïðîöåäóðû ×àñòîòíûé_ñëîâàðü Чтобы проверить макрос в действии, откроем произвольный документ. Затем, выбрав в строке меню Word команду Ñåðâèñ | Ìàêðîñ | Ìàêðîñû, следует выделить в списке Èìÿ пункт Частотный_словарь и щелкнуть на кнопке Âûïîëíèòü. В результате будет сформирован новый документ с частотным словарем (рис. 4.3) того документа, который был активным на момент запуска макроса.

Ñðàâíèòåëüíûé àíàëèç ïðàéñ-ëèñòîâ В предыдущих главах уже рассматривался прайс-лист ООО “Универсал”. Однако прайс-лист фирмы — не единственная из разновидностей прайс-листов, с которыми приходится иметь дело в офисе. Часто приходится иметь дело с прайс-листами других фирм, выступая при этом в роли клиента — потенциального покупателя и, как любой покупатель, обладать правом выбора. Очень часто право выбора превращается в необходимость выбрать наилучший по ценовым или другим показателям вариант — от эффективности закупок зависит, в конечном счете, успешное функционирование фирмы. Как и прайс-лист ООО “Универсал”, прайс-листы других фирм обычно выполнены в форме Excel-таблицы. Все они отличаются не только по содержанию, но и по оформлению. В разных офисах по-разному используют возможности Excel и, как следствие, среди реально существующих и обращающихся прайс-листов наблюдается поистине великое разнообразие. Если речь идет об одиночных товарных позициях, то автоматизация средствами VBA мало, чем поможет в анализе прайс-листов потенциальных или действующих поставщиков.

Рис. 4.4. Прайс-лист “А” Но для однородных товаров в широком ассортименте очень часто возникает нужда в сравнительном анализе двух или более прайс-листов, принадлежащих разным поставщикам. В разделе прайса, состоящем из нескольких десятков или, тем более, сотен позиций, не так-то просто найти нужное наименование. Если же речь идет о нахождении соответствующей позиции в других прайс-листах и сравнении цен, то выполнить такую операцию вручную можно только для одного-

120 Глава 4. Практикум программирования на VBA для Excel и Word нескольких товаров. Для десятков и сотен товарных позиций выполнить сравнительный анализ по нескольким прайс-листам возможно только при помощи средств автоматизации. Попробуем решить такую задачу “силами” VBA.

Âõîäÿùèå ïðàéñ-ëèñòû Предположим, что в работе используются прайс-листы двух фирм-поставщиков. По сравнению с внутренним прайс-листом, который является исходящим, прайс-листы других фирм будут входящими. Как нетрудно будет убедиться, решение задачи для двух прайс-листов без труда можно распространить на любое число прайсов. Пусть это будут прайс-листы по уже привычной для нас теме — расходным материалам. И пусть интерес вызывает раздел, касающийся картриджей для принтеров. Только на этот раз надо взглянуть на данный раздел с точки зрения потенциального покупателя. Возьмем в качестве материала для упражнений два прайс-листа, принадлежащих реальным фирмам, которые занимаются продажей офисных расходных материалов и комплектующих. Первый прайс лист (назовем его “А”), содержит раздел “Картриджи к струйным принтерам EPSON”, при этом в столбце “Part Number” отображается техническое наименование картриджа, а в столбце “Розница” — розничная цена в пересчете на доллары США (см. рис. 4.4). Другой прайс лист (назовем его “Б”), устроен несколько иным образом. То же самое техническое наименование отображается здесь в столбце “Артикул”, а розничной цене в пересчете на доллары США соответствует четвертый ценовой столбец (первые три столбца предназначены для покупателей, обладающих некоторой “историей покупок”). Фрагмент этого прайс-листа изображен на рис. 4.5.

Рис. 4.5. Прайс-лист ”Б”

Ïîñòàíîâêà çàäà÷è Как сопоставить эти прайс-листы? Или, поставим задачу шире — как обеспечить их автоматическое сопоставление − ведь фирмы “А” и “Б” регулярно обновляют свои прайсы и было бы очень неплохо, если бы сотрудник ООО “Универсал” мог отслеживать, как абсолютные, так и относительные изменения в прайс листах. Кроме изменений разницы в цене по некоторой позиции между прайс-листами разных фирм (относительные изменения) менеджера по закупкам могут интересовать изменения в ценах одной и той же фирмы, то есть разница в ценах между двумя версиями одного и того же прайса (абсолютные изменения). Иными словами, требуется создать универсаль-

Сравнительный анализ прайс-листов 121 ный механизм сравнения произвольных прайс-листов, который можно было бы применять, сообразуясь с текущими потребностями офиса. Если бы все фирмы поставляли один и тот же набор картриджей, то проблема решалась бы просто: достаточно было бы отсортировать два прайса по наименованию картриджа в алфавитном порядке и поместить рядом соответствующие столбцы. При этом позиции всех товаров в обоих прайсах совпали бы. Однако на практике, конечно, все обстоит сложнее. Даже если абсолютное большинство позиций присутствуют в обоих прайс-листах одновременно, нескольких отличающихся позиций будет достаточно, чтобы списки не совпали ни при какой сортировке. И тут уж не обойтись без помощи VBA….

Ñâîäíûé ïðàéñ-ëèñò Что может сделать макрос VBA в такой ситуации? Он может создать сводный список по двум или более прайс-листам, где присутствовали бы все позиции, встречающиеся в анализируемых прайсах. При этом каждая позиция будет встречаться только один раз, и соответствующие этой позиции цены, для всех прайс-листов, где такая позиция имеется, будут расположены в одной строке. После этого сопоставление цен по анализируемым прайс-листам станет совершенно тривиальной задачей, которую можно решить, как при помощи VBA, так и обычными средствами рабочего листа Excel. Можно, например, задать условное форматирование для соответствующих столбцов, которое меняло бы вид ячеек в зависимости от соотношения цен. Другой вариант — добавление столбца с “разностными” формулами, где соотношение будет видно по знаку разницы. Наконец, такой список нетрудно проанализировать, просто взглянув на него на экране.

Ïîäêëþ÷åíèå àíàëèçèðóåìûõ ïðàéñ-ëèñòîâ Чтобы создать универсальный механизм сопоставления прайс-листов, нужно абстрагироваться от конкретных деталей, которые могут отличаться в реальных случаях. Здесь возможны различные подходы. Например, можно связать посредством технологии Automation некоторые столбцы в анализируемых прайс-листах со сводным прайсом, с тем, чтобы любые изменения в исходных таблицах автоматически отображались в рабочей книге, служащей носителем сводного прайслиста. Другой возможный подход заключается в том, чтобы ориентироваться на абстрактные именованные диапазоны — если макрос будет иметь дело с диапазонами, скажем, “Наименование” и “Цена”, то внутреннее устройство любого конкретного прайс-листа не будет иметь значения. Достаточно будет указать в каждом прайс-листе диапазоны с такими именами, и для выполнения макроса больше ничего не потребуется. Начнем с того, что укажем такие диапазоны в прайс-листах, выбранных для упражнения. В прайс листе “А” диапазону “Наименование” соответствуют ячейки, начиная с ячейки C20 и ниже, вплоть до конца раздела. Диапазон “Цена” задается выделением ячеек, начиная с ячейки F20 и ниже, до конца раздела. Например, для задания именованного диапазона “Цена” необходимо, выделив ячейки, щелкнуть мышью в поле имени и ввести туда строку “Цена”, как это показано на рис. 4.6. Аналогичные дейчтвия необходимо проделать с прайс-листом “Б“. Это позволит абстрагироваться от деталей конкретного устройства различных прайс-листов и в роли прайсов “А“ и “Б“ смогут выступить любые прайс-листы, где указаны соответствующие именованные диапазоны. ← см. также в гл. 2 раздел “Именованные диапазоны”, где описан еще один метод создания именованного диапазона.

122 Глава 4. Практикум программирования на VBA для Excel и Word

Рис. 4.6. На рабочем листе прайса заданы именованные диапазоны ”Наименование“ и “Цена“

Ïîäãîòîâêà ðàáî÷åãî ëèñòà äëÿ ñâîäíîãî ïðàéñà Далее нам следует подготовить рабочий лист, на котором должен отображаться сводный прайс. Лист должен содержать столбец “Наименование“, отформатированный для ввода текстовых значений, и два (или более, по числу анализируемых прайсов) столбца с ценами, которые необходимо отформатировать для ввода значений в соответствующем денежном формате. Примерный вид такого рабочего листа изображен на рис. 4.7.

Рис. 4.7. Рабочий лист для отображения сводного прайс-листа Для создания и запуска макроса выберем простейший способ, предпочтение которому следует отдать в силу его универсальности: макрос с именем Сводка следует создать при помощи команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû (необходимо ввести в поле Èìÿ ìàêðîñà строку “Сводка” и щелкнуть на кнопке Ñîçäàòü), а запустить его можно будет опять же при помощи команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû (для этого необходимо выделить в списке имен строку “Сводка” и щелкнуть на кнопке Âûïîëíèòü). ÏÐÈÌÅ×ÀÍÈÅ

Конечно, в зависимости от конкретных обстоятельств, вопрос запуска макроса можно решить и по-другому. Если рабочий лист, на котором предполагается создать сводный прайс, является одним из нескольких листов рабочей книги, то код макроса можно поместить в обработчик события рабочего листа Activate, при этом макрос будет выполняться в момент, когда лист станет текущим. Если этот лист единственный в книге, то событие Activate для него при открытии книги

Сравнительный анализ прайс-листов 123 не наступит. Исходный текст макроса в этом случае можно поместить в обработчик события рабочей книги Open (процедура обработки этого события должна называться Workbook_Open) — тогда макрос будет выполняться при открытии рабочей книги. Чтобы создать обработчик этого события, необходимо выбрать двойным щелчком в окне проекта Visual Basic элемент ThisWorkbook, а в списках окна кода — элементы Workbook и Open.  см. также в приложении раздел “Как открыть файл (оператор Open)”.

Словом, здесь возможны различные варианты, для этого же примера был выбран простейший, который является в данном случае самым универсальным. Итак, создав тем или иным образом заготовку процедуры, приступим к разработке исходного текста макроса, который должен формировать сводный прайс-лист на основе прайсов “А” и “Б”.

Ñîçäàíèå ïðîöåäóðû Ñâîäêà() Вначале необходимо объявить объектные переменные, которые будут содержать в себе прайслисты “А” и “Б”, а также сводный прайс-лист. Назовем их, соответственно, PriceA, PriceB и PriceSvod. Объявить их необходимо как Worksheet, поскольку речь идет о рабочих листах. Целочисленные переменные A, B и SvodCount будут использованы в качестве счетчиковуказателей строк для соответствующих листов — их также необходимо предварительно объявить как Integer. Первый шаг состоит в том, чтобы создать переменные-объекты, заключающие в себе рабочие листы Excel, которые и будут обрабатываться. Поскольку предполагается, что рабочий лист сводного прайса является текущим, можно обратиться к нему, как к объекту ActiveSheet: Set PriceSvod = ActiveSheet Будем считать, что подлежащие анализу прайсы “А” и “Б” размещены на рабочих листах “Прайс-А” и “Прайс-Б”, которые находятся в книгах PriceA.xls и PriceB.xls, которые, в свою очередь, находятся в корневом каталоге диска C:. В этом случае можно создать переменные PriceA и PriceB следующим образом: Set PriceA = Workbooks.Open("C:\PriceA.xls").Worksheets("Прайс-А") Set PriceB = Workbooks.Open("C:\PriceB.xls").Worksheets("Прайс-Б") Вызовом метода Open семейства рабочих книг (Workbooks) в это семейство добавляется книга, содержащаяся в указанном файле. И уже в семействе рабочих листов открытой книги (Worksheets) оператор Set найдет необходимый лист и присвоит его в качестве значения соответствующей переменной. Счетчик строк сводного прайса необходимо установить на строку 2, чтобы пропустить заголовок: SvodCount = 2 Теперь все готово для просмотра прайс-листа “А” и сопоставления его с прайс-листом “Б”. Чтобы просмотреть все строки диапазона “Наименование” в листе “А”, используем цикл For: For A = 1 To PriceA.Range("Наименование").Rows.Count ... Next A Свойство Count семейства строк диапазона (Rows) задает верхнюю границу просмотра, поскольку в нем содержится количество строк в диапазоне (это число может меняться при вставке и удалении строк, и потому заранее неизвестно).

124 Глава 4. Практикум программирования на VBA для Excel и Word Первое, что следует сделать при просмотре очередной строки прайса “А”, это скопировать ее в соответствующую позицию сводного прайса, поскольку в одном из анализируемых прайс-листов эта позиция уже встретилась: PriceSvod.Cells(SvodCount, 1) = _ PriceA.Range("Наименование").Cells(A, 1) PriceSvod.Cells(SvodCount, 2) = PriceA.Range("Цена").Cells(A, 1) Копирование осуществляется в 1 и 2 столбцы SvodCount-й строки, то есть в столбцы, соответствующие наименованию товара и его цене по прайсу “А”. Тут же, не покидая SvodCount-й строки сводного прайс-листа, необходимо просмотреть прайс “Б”: а нет ли там такой же позиции? Если наименование одной из позиций прайса “Б” совпало с текущей позицией прайса “А”, то соответствующее ей значение цены необходимо добавить в сводный прайс-лист, теперь уже в 3-й столбец, который соответствует ценам прайса “Б”: For B = 1 To PriceB.Range("Наименование").Rows.Count If PriceB.Range("Наименование").Cells(B, 1) = _ PriceA.Range("Наименование").Cells(A, 1) _ Then PriceSvod.Cells(SvodCount, 3) = _ PriceB.Range("Цена").Cells(B, 1) PriceB.Range("Наименование").Cells(B, 1).Value = "" End If Next B Обратите внимание на тот факт, что во втором операторе присваивания после Then совершается еще одно действие, необходимость в котором станет понятной в дальнейшем. Чтобы исключить из рассмотрения ту позицию прайса “Б”, цена которой была только что зарегистрирована в сводном прайс-листе, необходимо записать в столбец “Наименование” пустую строку. Теперь эта позиция повторно уже не “найдется”. Если бы сравнительному анализу подвергались не два, а три или более прайс-листов, то в этом месте необходимо было бы выполнить аналогичные действия для прайсов “В”, “Г” и т.д. Наконец, нужно передвинуть счетчик-указатель строки сводного листа на следующую строку: SvodCount = SvodCount + 1 После того, как переменная A “дойдет” до конца диапазона “Наименование” прайс-листа “А”, на рабочем листе сводного прайса будет сформирован список всех позиций “А”, причем для тех из них, которые обладают “двойником” в листе “Б”, в 3-м столбце будет указана цена прайса “Б”. Но задача еще не решена! В прайсе “Б” могут найтись позиции, которых не было в прайсе “А”. Чтобы обнаружить таковые, нужно просмотреть теперь весь диапазон “Наименование” в прайсе “Б”. В этом случае пригодится проявленная ранее предусмотрительность: все уже помещенные в сводку позиции из прайса “Б” исключены (для них в столбец “Наименование” была записана пустая строка), поэтому нет необходимости в дополнительных проверках — просто можно просмотреть лист “Б” точно так же, как это было сделано с листом “А”. Единственная особенность, которую необходимо здесь учесть, состоит в том, что вначале необходимо проверить, существуют ли очередная позиция. Иными словами, не “вычеркнута“ ли она из рассмотрения по той причине, что она встретилась при просмотре листа “А” и уже помещена в сводный прайс-лист. Таким образом, конструкцию цикла For необходимо дополнить наложением условия “ячейка в столбце наименования не пуста”: For B = 1 To PriceB.Range("Наименование").Rows.Count If PriceB.Range("Наименование").Cells(B, 1) "" Then

Сравнительный анализ прайс-листов 125 ... End If Next B Точно таким же образом в этом цикле в сводный прайс будет занесена любая встретившаяся непустая позиция: PriceSvod.Cells(SvodCount, 1) = _ PriceB.Range("Наименование").Cells(B, 1) PriceSvod.Cells(SvodCount, 3) = PriceB.Range("Цена").Cells(B, 1) Затем, по логике копирования, необходимо проверить, нет ли такой позиции в прайсе “А”: For A = 1 To PriceA.Range("Наименование").Rows.Count If PriceA.Range("Наименование").Cells(A, 1) = _ PriceB.Range("Наименование").Cells(B, 1) _ Then PriceSvod.Cells(SvodCount, 2) = _ PriceA.Range("Цена").Cells(A, 1) End If Next A Однако в этом, конечно же, нет никакой нужды — все позиции прайса “А” уже просмотрены и включены в сводный список. Впрочем, если бы анализировали три или более прайс-листов, то проверку остальных, не просмотренных еще списков, в этом месте следовало бы выполнить. Завершаться каждый проход этого цикла должен очередным шагом счетчика-указателя строк в сводном прайс-листе: SvodCount = SvodCount + 1 В основном, дело сделано. Для того чтобы улучшить визуальное восприятие сводного прайслиста, можно выделить жирным шрифтом те значения цены, которые меньше соответствующего значения в другом прайс-листе. Для этого нужно во всех местах, где в ячейки записываются значения цены, выполнить проверку и изменить свойство Bold объекта Font, принадлежащего соответствующей ячейке. Например, это можно было бы сделать следующим образом: If PriceSvod.Cells(SvodCount, 2) < PriceSvod.Cells(SvodCount, 3) Then PriceSvod.Cells(SvodCount, 2).Font.Bold = True Else PriceSvod.Cells(SvodCount, 3).Font.Bold = True End If

Àâòîìàòè÷åñêîå çàêðûòèå ôàéëà ñ îòìåíîé ñäåëàííûõ â íåì èçìåíåíèé Наконец, необходимо закрыть использованные рабочие листы и сделать текущим сводный прайс-лист. Проблема заключается в том, что такой объект, как рабочий лист закрыть нельзя. Закрыть можно рабочую книгу, где он содержится. Ссылка на “свою” рабочую книгу у каждого листа Excel доступна через свойство Parent: PriceA.Parent.Close SaveChanges:=False PriceB.Parent.Close SaveChanges:=False Обратите внимание на логический параметр SaveChanges — задав ему значение False, тем самым можно отменить сохранение изменений в файле рабочей книги. Благодаря этому запись пустых строк в столбец “Наименование” не будет иметь последствий.

126 Глава 4. Практикум программирования на VBA для Excel и Word  см. также в приложении раздел “Закрытие файлов (операторы Reset, Close)”.

Кроме этого, следует присвоить объектным переменным значение Nothing, чтобы освободить занятую ими память: Set PriceA = Nothing Set PriceB = Nothing  см. также в гл. 3 раздел “Освобождение системной памяти компьютера”.

И вот, макрос готов. Полный его исходный текст представлен в листинге 4.4. ËÈÑÒÈÍÃ 4.4

Sub Сводка() Dim PriceA As Worksheet Dim PriceB As Worksheet Dim PriceSvod As Worksheet Dim A, B, SvodCount As Integer Set PriceSvod = ActiveSheet Set PriceA = Workbooks.Open("C:\PriceA.xls").Worksheets("Прайс-А") Set PriceB = Workbooks.Open("C:\PriceB.xls").Worksheets("Прайс-Б") SvodCount = 2 For A = 1 To PriceA.Range("Наименование").Rows.Count PriceSvod.Cells(SvodCount, 1) = _ PriceA.Range("Наименование").Cells(A, 1) PriceSvod.Cells(SvodCount, 2) = _ PriceA.Range("Цена").Cells(A, 1) For B = 1 To PriceB.Range("Наименование").Rows.Count If PriceB.Range("Наименование").Cells(B, 1) = _ PriceA.Range("Наименование").Cells(A, 1) _ Then PriceSvod.Cells(SvodCount, 3) = _ PriceB.Range("Цена").Cells(B, 1) PriceB.Range("Наименование").Cells(B, 1).Value = "" End If Next B If PriceSvod.Cells(SvodCount, 2) < PriceSvod.Cells(SvodCount, 3) Then PriceSvod.Cells(SvodCount, 2).Font.Bold = True Else PriceSvod.Cells(SvodCount, 3).Font.Bold = True End If SvodCount = SvodCount + 1 Next A For B = 1 To PriceB.Range("Наименование").Rows.Count

Сравнительный анализ прайс-листов 127

If PriceB.Range("Наименование").Cells(B, 1) "" Then PriceSvod.Cells(SvodCount, 1) = _ PriceB.Range("Наименование").Cells(B, 1) PriceSvod.Cells(SvodCount, 3) = _ PriceB.Range("Цена").Cells(B, 1) For A = 1 To _ PriceA.Range("Наименование").Rows.Count If PriceA.Range("Наименование").Cells(A, 1) = _ PriceB.Range("Наименование").Cells(B, 1) _ Then PriceSvod.Cells(SvodCount, 2) = _ PriceA.Range("Цена").Cells(A, 1) End If Next A If PriceSvod.Cells(SvodCount, 2) < _ PriceSvod.Cells(SvodCount, 3) _ Then PriceSvod.Cells(SvodCount, 2).Font.Bold = True Else PriceSvod.Cells(SvodCount, 3).Font.Bold = True End If SvodCount = SvodCount + 1 End If Next B PriceA.Parent.Close SaveChanges:=False PriceB.Parent.Close SaveChanges:=False Set PriceA = Nothing Set PriceB = Nothing PriceSvod.Activate End Sub Чтобы проверить макрос в действии, необходимо открыть сводный прайс-лист и выполнить макрос Сводка при помощи команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû. Результат выполнения макроса изображен на рис. 4.8.

128 Глава 4. Практикум программирования на VBA для Excel и Word

Рис. 4.8. Сводный прайс-лист автоматически сформирован на основе данных прайс-листов “А“ и “Б“

Îñîáåííîñòè èñõîäíûõ äàííûõ èç ñðàâíèâàåìûõ ïðàéñ ëèñòîâ Как уже отмечалось выше, в оформлении реально обращающихся прайс-листов наблюдается поистине великое разнообразие. Вполне возможен случай, когда наименование товара в одном из подлежащих анализу прайс-листов устроено совершенно иным образом. Например, в изображенном на рис. 4.9 прайсе техническое наименование картриджа включено в состав строки общего наименования. Вообще говоря, для каждой подобной особенности в исходных данных потребуется специальное решение. В данном случае, например, можно воспользоваться тем фактом, что техническое наименование находится повсюду в одной и той же позиции строки общего наименования. Поэтому, при сравнении строковых значений можно применить функцию Mid, извлекая из сравниваемой строки 7 символов начиная с 16-го — это и будет в данном случае сопоставляемое наименование картриджа с наименованиями из других прайсов: If Mid(PriceB.Range("Наименование").Cells(B, 1), 16, 7)= _ PriceA.Range("Наименование").Cells(A, 1) _ ...

Сравнительный анализ прайс-листов 129

Рис. 4.9. Вариант прайс-листа, в котором технические подробности о товаре находятся в одной ячейке с его наименованием

Глава 5 Access: ñîçäàíèå îôèñíîé áàçû äàííûõ В этой главе будет рассмотрена работа с самым профессиональным приложением MS Office — системе управления базами данных (СУБД) MS Access. Конечно, эта программа предназначена для работы с базами данных не более чем офисного масштаба, но все же MS Access менее всех других приложений Office пригоден для непрофессиональных пользователей. Сказанное, однако, не означает, что только профессиональным программистам принадлежит монополия на работу в среде Access. Достаточно опытный пользователь, как вскоре можно будет убедиться, в состоянии многое сделать при помощи тех средств MS Access, которые не требуют знания теории баз данных и наличия серьезных навыков программирования. Подобно рабочим книгам Excel, база данных Access позволяет работать с табличными данными, систематизированными каким-нибудь образом. Однако есть два основных отличия. Так легко, наглядно и просто, как это выглядит на рабочем листе Excel, работать с таблицами Access не удастся — здесь все сложнее по определенным првилам. Второе отличие заключается в “мощности” рабочих инструментов — средства Access обладают несопоставимо большими функциональными возможностями и позволяют решать абсолютно любые задачи обработки данных в масштабах офиса, не стесняя пользователя никакими ограничениями. Этих данных может быть сколь угодно много, и структура их может быть сколь угодно сложна.

Ñîçäàíèå áàçû äàííûõ MS Access Вначале создадим небольшую и несложную базу данных, а затем займемся автоматизацией работы с этой базой данных при помощи VBA. Первый, самый простой шаг, состоит в создании пустой базы данных, с которой затем и предполагается дальше работать. После запуска MS Access на экране появится диалоговое окно, позволяющее выполнить одно из следующих действий: • открыть один из существующих файлов с базой данных — переключатель Îòêðûòü áàçó äàííûõ; • создать базу данных с помощью мастера или на основе готового проекта — переключатель Ìàñòåðà, ñòðàíèöû è ïðîåêòû áàç äàííûõ; • создать пустую базу данных “без изысков” — переключатель Íîâàÿ áàçà äàííûõ. Для нашего примера нужна пустая база данных. Для этого установите переключатель Íîâàÿ áàçà äàííûõ и щелкните на кнопке ÎÊ.

В открывшемся диалоговом окне задайте имя файла новой базы данных, — например, пусть он называется MyOffice.mdb, и щелкните на кнопке ÎÊ. Первый шаг на пути освоения MS Access сделан. Теперь можно приступать к упражнениям.

Ñîçäàíèå òàáëèö Основа любой базы данных — это таблицы с данными. В состав базы можно затем включить отчеты и запросы, экранные формы и программные модули, но без таблиц с данными никакой базы не получится.

132 Глава 5. Access: создание офисной базы данных ÏÐÈÌÅ×ÀÍÈÅ

Программа MS Access из пакета Office 2000 в больше отличается от своих предшественников, чем Word или Excel. Также изменения коснулись основ работы с Visual Basic для приложений — теперь в Access есть явный доступ к редактору Visual Basic. Формат базы данных Access претерпел серьезные изменения — вплоть до полной несовместимости с предыдущими версиями. Наконец, существенно изменился сам вид окна базы данных. В левой части окна базы данных расположена панель инструментов с кнопками, переключающими вкладки окна. Обратимся к вкладке Òàáëèöû — именно с создания таблиц начнем работу с базой данных (рис. 5.1).

Рис. 5.1. Окно базы данных

Âñïîìîãàòåëüíûå òàáëèöû−ñïðàâî÷íèêè Создааемая здесь простейшая база данных будет состоять из двух основных таблиц. В первой, назовем ее “Клиенты”, будут накапливаться сведения о клиентах фирмы. Вторая таблица будет содержать данные о приходных и расходных операциях, связанных с этими клиентами. Но прежде, чем приступить к созданию сравнительно сложных основных таблиц, необходимо подготовить несколько простых таблиц-справочников. Эти справочники будут использованы затем при создании основных таблиц.

Ñîçäàíèå ñïðàâî÷íèêà êëèåíòîâ ôèðìû Например, будем подразделять клиентов на несколько категорий и для каждой записи в таблице “Клиенты” укажем одну из категорий. Допустим, среди клиентов надо различать “реализаторов”, “постоянных покупателей” и “случайных клиентов” — для каждого предусмотрим отличающиеся условия (например, разные величины скидок). Чтобы можно было впоследствии без труда дополнять и модифицировать набор категорий в соответствии с текущими нуждами офиса, вынесем этот набор в отдельную таблицу-справочник. С этого и начнем. Êàê ñîçäàòü òàáëèöó â ðåæèìå êîíñòðóêòîðà 1. Щелкните на кнопке Таблицы в панели инструментов окна базы данных. 2. Выберите двойным щелчком в окне базы данных значок Создание таблицы в режиме Конструктора. В результате откроется окно конструктора таблиц. 3. В первой ячейке столбца Имя поля введите строку “КодТипа”, а в соответствующей (то есть первой) ячейке столбца Тип данных разверните список и выберите Счетчик. 4. Командой Правка | Ключевое поле сделайте это поле ключевым (аналогичного результата можно достичь, щелкнув правой кнопкой мыши на первой строке и выбрав команду Ключевое поле в контекстном меню).

Создание таблиц 133 Во второй ячейке столбца Имя поля введите строку “ТипКлиента”, затем выберите для этого поля тип данных — Текстовый. 6. На вкладке Общие в нижней части окна конструктора укажите в поле Размер поля значение 25 (по умолчанию там содержится значение 50). В результате окно конструктора таблиц должно приобрести вид, как на рис. 5.2.

5.

Рис. 5.2. Окно конструктора таблиц: создано два поля Закройте окно конструктора командой Ôàéë | Çàêðûòü или щелчком на кнопке закрытия в правом верхнем углу окна. При этом MS Access выдаст запрос: “Сохранить изменения макета или структуры таблицы Таблица1?” (рис. 5.3). В ответ следует щелкнуть на кнопке Äà.

Рис. 5.3. Запрос на сохранение макета или структуры таблицы выдается не только при закрытии окна конструктора, но и в некоторых других случаях В появившемся затем диалоговом окне Ñîõðàíåíèå введите в поле Èìÿ òàáëèöû строку “ТипКлиента” и щелкните на ÎÊ. В результате в окне базы данных, на вкладке Òàáëèöû, появится значок таблицы ÒèïÊëèåíòà. Откройте эту таблицу двойным щелчком мыши и введите в нее несколько строк (в В поле ÊîäÒèïà вводить ничего не нужно — значения в этом поле формируются автоматически. В результате таблица должна приобрести вид, как на рис. 5.4.

Рис. 5.4. Простейшая таблица-справочник готова, в нее теперь можно ввести “справочные” данные

134 Глава 5. Access: создание офисной базы данных Затем эта таблица будет использована в качестве столбца подстановки в других таблицах, и если нужно будет изменить набор категорий, на которые подразделяются клиенты, то достаточно будет открыть таблицу “ТипКлиента” и внести в нее необходимые изменения — во всей остальной базе данных ничего менять не потребуется.

Ñîçäàíèå ñïðàâî÷íèêà îáðàùåíèé ê êëèåíòàì ôèðìû Далее необходимо создать еще один справочник. Если использовать базу данных для автоматического формирования писем или сообщений для клиентов, то потребуется использовать в каждом случае какое-то обращение. К хорошо знакомому человеку обращаются не так, как к незнакомому, а к женщине — иначе, чем к мужчине. Создайте при помощи конструктора таблиц еще один справочник под названием “Обращение”, руководствуясь инструкциями, приведенными в предыдущем разделе. Таблица должна содержать поля “КодОбращения” и “Обращение”. Введите затем в созданную таблицу несколько видов обращения: например, пусть это будут “Уважаемый”, “Уважаемая”, “Дорогой” и “Дорогая”. С созданием вспомогательных таблиц-справочников подготовительный этап закончен, и можно приступать к созданию основных таблиц.

Òàáëèöà “Êëèåíòû” Первые несколько полей таблицы не таят в себе сложностей. Просто запустите вновь конструктор таблиц и создайте ключевое поле “КодКлиента” с типом данных Ñ÷åò÷èê, текстовые поля “Фамилия”, “Имя” и “Отчество”, задав для каждого из них размер поля — 15. Следующее поле, пятое по счету, будет содержать дату рождения. Его следует назвать “Дата Рождения”, а в столбце Òèï äàííûõ выбрать для него тип Äàòà/âðåìÿ. Затем, в области свойств поля (область в нижней части окна конструктора), на вкладке Îáùèå, необходимо выбрать в строке Ôîðìàò ïîëÿ формат Êðàòêèé ôîðìàò äàòû. Строкой ниже, в поле Ìàñêà ââîäà, щелкните на кнопке построителя (кнопка с тремя точками, которая видна только тогда, когда строка выбрана). В следующий момент необходимо будет подтвердить сохранение таблицы — сохраните таблицу под именем “Клиенты”. Затем, в диалоговом окне Ñîçäàíèå ìàñîê ââîäà выберите маску Êðàòêèé ôîðìàò äàòû, как показано на рис. 5.5 и щелкните на кнопке Ãîòîâî. Далее необходимо создать еще два простых текстовых поля — это должны быть поля “Адрес” и “E-Mail”, с размером, соответственно, 50 и 20. Следующее поле — “Скидка”. Здесь будет храниться величина скидки для клиента в процентном выражении. Такое поле можно было бы включить в состав справочника типов клиентов, однако при этом оказалось бы невозможным задание для отдельного клиента “индивидуальной скидки”.

Рис. 5.5. Диалоговое окно создания масок

Создание таблиц 135 Задайте имя поля — “Скидка” и выберите для него тип данных — ×èñëîâîé. В свойствах поля укажите формат поля — Ïðîöåíòíûé, и размер поля — Îäèíàðíîå ñ ïëàâàþùåé òî÷êîé, как показано на рис. 5.6.

Рис. 5.6. Свойства поля “Скидка” нуждаются в дополнительной настройке

Ïîäñòàíîâî÷íûå ïîëÿ Теперь предстоит создать два поля посложнее. В этих подстановочных полях должны будут содержаться коды: код типа клиента и код обращения. Однако, хотя физически в полях будут записаны числовые значения, отображаться на экране будут соответствующие им строки из справочников. Задайте в качестве имени следующего поля строку “КодТипа” — это имя совпадет с именем ключевого столбца в таблице “ТипКлиента”. В качестве типа данных выберите в списке пункт Ìàñòåð ïîäñòàíîâîê. Это приведет к запуску специального мастера, который поможет создать подстановочное поле. Êàê ñîçäàòü ïîäñòàíîâî÷íûå ïîëÿ ïðè ïîìîùè Ìàñòåðà ïîäñòàíîâîê 1. В первом диалоговом окне Создание подстановки просто щелкните на кнопке Далее. Переключатель способа подстановки должен находиться в позиции Объект "столбец подстановки" будет использовать значения из таблицы или запроса, как по умолчанию (рис. 5.7).

136 Глава 5. Access: создание офисной базы данных

Рис. 5.7. Первый шаг мастера подстановок На следующем шаге мастера выделите в списке имеющихся таблиц пункт “ТипКлиента” (именно эта таблица будет играть роль столбца подстановки) и щелкните на кнопке Далее. 3. На следующем шаге мастера выделите в списке Доступные поля пункт “ТипКлиента” и щелчком мыши на кнопке [>] переместите его в список Выбранные поля, как показано на рис. 5.8. Завершите шаг мастера щелчком на кнопке Далее.

2.

Рис. 5.8. Выбор полей для подстановки В следующем диалоговом окне необходимо будет задать ширину столбца, растянув правую границу при помощи мыши так, чтобы самая длинная строка была полностью видна (рис. 5.9). 5. Наконец, на последнем шаге мастера введите в поле Задайте подпись строку “Тип клиента”. Щелкните после этого на кнопке Готово, и создание подстановочного поля будет завершено. 6. В появившемся окне сообщения подтвердите сохранение таблицы щелчком на кнопке Да. 4.

Создание таблиц 137

Рис. 5.9. Определение ширины подстановочного столбца Далее следует аналогичным образом создать подстановочное поле “КодОбращения” — для него в качестве столбца подстановки необходимо использовать таблицу “Обращение”. Закрыв после всего этого окно конструктора легко можно убедиться в том, что подстановочные, как впрочем, и все остальные, поля работают. Откройте таблицу “Клиенты” двойным щелчком на ее значке и попробуйте ввести значения в ее поля. Можно убедиться, что поле “День рождения” проверяет вводимые значения и допускает ввод только корректных значения даты, что поле “Скидка” при вводе в него дробных чисел автоматически преобразует их в процентный формат, а поля “Тип Клиента” и “Обращение” раскрывают список значений для выбора, как показано на рис. 5.10.

Рис. 5.10. Подстановочный столбец в действии

Òàáëèöà “Îïåðàöèè” Второй основной таблицей будет таблица “Операции”. Здесь будут регистрироваться операции, связанные с клиентами. Каким должен быть набор возможных операций — зависит в значительной степени от специфики конкретного офиса. Предположим, что в отношениях между клиентами и фирмой различаются одна расходная операция — отпуск товара и одна приходная — получение платежа. Чтобы не ограничиваться таким набором операций, поле типа операции следует сделать подстановочным и использовать в качестве источника подстановочных данных отдельную таблицу — справочник типов операций.

Ñîçäàíèå ñïðàâî÷íèêà òèïîâ îïåðàöèé Итак, перед созданием таблицы “Операции” необходимо подготовить таблицу-справочник. Пусть она называется “ТипОперации” и пусть в ней содержатся два поля — ключевое поле типа Ñ÷åò÷èê с

138 Глава 5. Access: создание офисной базы данных именем “КодОперации” и текстовое поле “Операция”. Полагаем, читатель уже в состоянии без больших усилий создать такую таблицу при помощи конструктора. В созданную таблицу необходимо ввести две записи (речь идет, скорее, о двух строках в поле “Операция”, поскольку, напомним, числовые значения в поле-счетчике “КодОперации” формируются автоматически). Пусть расходная операция называется “Отпуск товара”, а приходная — “Получение платежа”. Кстати сказать, есть прямой смысл придерживаться при дальнейших дополнениях справочника одного и того же порядка четности/нечетности — пусть все расходные операции в дальнейшем обладают нечетными кодами, а приходные — четными. Такое построение справочника может оказаться полезным при дальнейшем развитии базы данных. Поэтому в справочнике обращений нечетные коды были присвоены обращениям в мужском роде и четные — обращениям в женском роде.

Ñîçäàíèå òàáëèöû îïåðàöèé После того, как справочник типов операций подготовлен, можно приступить к созданию непосредственно таблицы “Операции”. В результате описанных ниже действий окно конструктора должно приобрести вид, как на рис. 5.11

Рис. 5.11. Таблица “Операции” в окне конструктора Первым полем должно быть поле “Дата” — его следует отформатировать для ввода значений даты. 2. Далее следует поле “ПорядковыйНомер” — задайте для него тип данных Ñ÷åò÷èê и сделайте ключевым полем. 3. Для создания поля “КодОперации” необходимо использовать подстановку из таблицы “ТипОперации”. Действуйте по схеме описанной при создании поля “ТипКлиента” в таблице “Клиенты”.

1.

 см. в этой главе раздел “Создание справочника клиентов фирмы”.

Выберите в качестве типа данных пункт “Мастер подстановок”. На втором шаге мастера выберите таблицу “ТипОперации”. Затем переместите в список Âûáðàííûå ïîëÿ пункт “Операция” из списка Äîñòóïíûå ïîëÿ. Укажите ширину столбца подстановки, а на последнем шаге мастера задайте подпись столбца — “Операция”. 5. Точно таким же образом создается подстановочное поле “КодКлиента”, только в качестве источника подстановки следует использовать таблицу “Клиенты”, выбрав в ней (то есть, переместив в список Âûáðàííûå ïîëÿ) поле “Фамилия”, а в качестве подписи столбца использовав строку “Клиент”. 4.

Ввод данных в базу данных Access 139 6.

Наконец, для поля “Сумма” следует выбрать тип данных “Денежный”. При этом в поле ×èñëî äåñÿòè÷íûõ çíàêîâ на вкладке Îáùèå в нижней части окна конструктора введите

значение 2. Таблица “Операции” готова. Теперь в нее можно заносить для каждой клиента из таблицы “Клиенты” приходные и расходные операции, указывая их тип из справочника “ТипОперации”. Собственно говоря, готовы уже все необходимые таблицы и настала пора подумать о том, каким же образом в них будут попадать данные.

Ââîä äàííûõ â áàçó äàííûõ Access MS Access позволяет вводить данные непосредственно в таблицы, однако это способ “на крайний случай”. Данные в таблицы Access могут попадать многими способами, в том числе и в результате выполнения программного кода VBA в одном из приложений Office (далее это будет рассмотрено). Однако самый простой, удобный и понятный любому пользователю без дополнительных объяснений способ заключается в использовании экранных форм. На следующем этапе “строительства” базы данных создадим экранные формы для ввода данных в таблицы “Клиенты” и “Операции”. Также создадим вспомогательную (подчиненную) форму для формы “Клиенты”, которая будет отображать операции только по текущему клиенту.

Ñîçäàíèå ýêðàííûõ ôîðì Подобно таблицам и другим объектам Access, формы можно создавать как при помощи соответствующего мастера, так и в режиме конструктора. Работа в конструкторе форм Access не отличается простотой, поэтому этот способ будет рассмотрен несколько позднее. Вначале создадим простую форму, полностью положившись на возможности мастера форм.

Ôîðìà “Âñå îïåðàöèè” Щелчком на кнопке Ôîðìû переключите окно базы данных на вкладку Ôîðìû. Двойным щелчком на значке Ñîçäàíèå ôîðìû ïðè ïîìîùè ìàñòåðà запустите мастер форм.

Êàê ñîçäàòü ýêðàííóþ ôîðìó ïðè ïîìîùè Ìàñòåðà ôîðì В диалоговом окне Создание форм, на первом шаге мастера, в раскрывающемся списке Таблицы и запросы выберите пункт Таблица: Операции. Затем, при помощи кнопки [>], переместите все поля кроме поля “ПорядковыйНомер” из списка Доступные поля в список Выбранные поля, как показано на рис. 5.12, и щелкните на кнопке Далее. 2. На следующем шаге мастера установите переключатель в позицию Ленточный и щелкните на кнопке Далее. 3. Затем выберите стиль формы (в этом примере использован стиль Стандартный) и щелкните на кнопке Далее. 4. На последнем шаге мастера введите в поле Задайте имя формы строку “Все операции” и щелкните на кнопке Готово. 1.

140 Глава 5. Access: создание офисной базы данных

Рис. 5.12. Мастер форм − выбор полей таблицы для отображения в создаваемой форме

Ââîä äàííûõ ïðè ïîìîùè ôîðìû “Âñå îïåðàöèè” Вновь созданная форма автоматически открывается для ввода данных (см. рис. 5.13). В нее можно ввести некоторые данные и затем закрыть командой Ôàéë | Çàêðûòü или щелчком на кнопке закрытия окна формы в правом верхнем углу.

Рис. 5.13. Форма “Все операции“

Ïîä÷èíåííàÿ ôîðìà “Îïåðàöèè ïî êëèåíòó” Прежде чем приступать к созданию формы “Клиенты”, необходимо подготовить небольшую вспомогательную форму, которая будет использоваться для отображения операций по текущему клиенту. Это несложная форма, которую можно подготовить при помощи мастера форм. 1. Щелчком на кнопке Ôîðìû переключите окно базы данных на вкладку Ôîðìû. Двойным щелчком на значке Ñîçäàíèå ôîðìû ïðè ïîìîùè ìàñòåðà запустите мастер форм. 2. В раскрывающемся списке Òàáëèöû è çàïðîñû выберите пункт Òàáëèöà: Îïåðàöèè. 3. При помощи кнопки [>] переместите из списка Äîñòóïíûå ïîëÿ в список Âûáðàííûå ïîëÿ поля “Дата”, “КодОперации” и “СуммаОперации”, и щелкните на кнопке Äàëåå. 4. Выберите внешний вид формы — Òàáëè÷íûé и щелкните на кнопке Äàëåå. 5. Затем выберите стиль Ñòàíäàðòíûé и щелкните на кнопке Äàëåå.

Создание экранных форм 141 Задайте имя формы: “Операции по клиенту” и завершите работу мастера щелчком на кнопке Ãîòîâî. Пока что эта форма отображает все записи из таблицы “Операции”, однако, как таковая, она использоваться не будет. Ей предстоит стать частью другой формы. 7. Закройте созданную форму командой Ôàéë | Çàêðûòü.

6.

Ôîðìà “Êëèåíòû” Последняя из форм, которые необходимо создать, наиболее сложна. Создадим ее в два этапа: вначале при помощи мастера подготовим простую форму-“полуфабрикат”, а затем доработаем форму в режиме конструктора.

Ñîçäàíèå ïðîñòîé ôîðìû 1. 2. 3.

4. 5.

Двойным щелчком на значке Ñîçäàíèå ôîðìû ïðè ïîìîùè ìàñòåðà запустите мастер форм. В раскрывающемся списке Òàáëèöû è çàïðîñû выберите пункт Òàáëèöà: Êëèåíòû. Вначале, при помощи кнопки [>>] переместите все поля таблицы “Клиенты” из списка Äîñòóïíûå ïîëÿ в список Âûáðàííûå ïîëÿ, а затем верните обратно поле “КодКлиента” при помощи кнопки [ SumIn Then MsgBox("Отрицательное сальдо по клиенту " + _ Klients.Fields("Фамилия")) ... End If

Èñïîëüçîâàíèå ôóíêöèè MsgBox äëÿ îðãàíèçàöèè èíòåðôåñà, ïðåäîñòàâëÿþùåãî ïîëüçîâàòåëþ âûáîðî÷íî óïðàâëÿòü âûïîëíåíèåì ïðîãðàììû Функция MsgBox позволяет не только выводить сообщения, но и сформировать интерфейс для принятия пользователем решения. Например, при помощи константы-параметра vbYesNo можно создать в окне сообщения кнопки Äà и Íåò (рис. 6.6).

Рис. 6.6. Окно сообщения содержит кнопки Да и Нет Сравнивая возвращенное функцией MsgBox значение с соответствующими константами, можно определить, которую из кнопок выбрал пользователь: If MsgBox("Отрицательное сальдо по клиенту " _ + Klients.Fields("Фамилия") + _ ". Создать письмо?", vbYesNo, "Отрицательное сальдо") _ = vbYes Then ... ... End If Последний параметр функции MsgBox определяет здесь надпись в строке заголовка окна сообщения. Операторы между Then и End If получат управление только в том случае, если пользователь нажмет кнопку Äà. Внутри этой конструкции можно предпринять какие-то действия, например, сгенерировать готовое письмо данному клиенту. Вопрос о предпринимаемых действиях будет рассмотрен чуть ниже, а пока закончим проход по таблице “Клиенты”.  см. в этой главе раздел “Использование объектов Word для формирования письма из данных таблицы Access”.

После того, как цикл просмотра Recordset-объекта Klients выполнит свою задачу, необходимо закрыть таблицу “Клиенты”, к которой подключен объект, Klients.Close и освободить память от всех объектов:

160 Глава 6. Access: автоматизация офисной базы данных Set Klients = Nothing Set MyDb = Nothing На этом макрос завершает свою работу.

Ïðèíÿòèå ðåøåíèÿ ïî ðåçóëüòàòàì àíàëèçà äàííûõ Программный код, который был только что разработан, служит для анализа данных, накопленных в базе данных. Цели анализа могут быть самыми разными, например, можно учитывать даты операций, выделяя какие-то диапазоны времени, или выделять клиентов по размеру сумм операций. В каждой конкретной базе данных есть свои актуальные задачи и цели. Но в чем смысл и цель анализа данных? Что должно явиться результатом? Результат должен состоять в принятии каких-то решений и в выполнении каких-то действий. Действия эти могут быть самыми различными — в рассматриваемом примере логичным действием представляется создание письма нерадивому клиенту. Причем, поскольку данные текущего клиента у нас “под рукой”, а смысл ситуации вполне однозначен, содержание и реквизиты такого письма могут быть сгенерированы полностью автоматически. Для того чтобы и форматирование письма могло быть выполнено автоматически, потребуется использовать в VBA-коде модуля Access обращения к объектам Word. Итак, создадим VBA-код, который должен выполняться в рамках конструкции If MsgBox…= vbYes Then…, то есть в случае, когда пользователь при получении запроса на создание письма щелкнет на Äà.

Èñïîëüçîâàíèå îáúåêòîâ Word äëÿ ôîðìèðîâàíèÿ ïèñüìà èç äàííûõ òàáëèöû Access Прежде всего, следует задать ссылку на объектную библиотеку Word, поскольку предполагается обращаться к объектам Word из программного модуля Access. Полагаем, детали такой операции читателю напоминать уже не требуется. ← см. подробнее в гл. 2 раздел “Как задать ссылку на библиотеку объектов Word в среде MS Excel”.

Далее нужно объявить необходимые объектные переменные (сделать, это, конечно, лучше всего в начале кода функции): Dim WD As Word.Application Dim MyLetter As New LetterContent Переменная WD будет представлять в этом примере приложение Word (такие объекты уже рассматриварись в предыдущих главах).  см. также в приложении раздел “Объектные переменные”.

Îáúåêòû òèïà LetterContent Переменная MyLetter представляет собой нечто новое. (Новым является также слово New в объявлении этой переменной — благодаря этому слову экземпляр объекта будет создан автоматически, без каких-либо дополнительных усилий). Это специальный объект, служащий для создания писем. Объекты типа LetterContent являются своеобразными “переносчиками” параметров для Ìàñòåðà ïèñåì Word. Все свойства такого объекта представляют собой реквизиты письма. Заполнив эти свойства значениями, можно запустить мастер писем (метод RunLetterWizard) и передать ему в качестве параметра этот объект. При этом все диалоговые окна мастера окажутся уже заполненными и пользователю останется только щелкнуть на кнопке Ãîòîâî. Впрочем, можно обойтись и без появления мастера писем на экране, использовав метод SetLetterContent активного документа, — при этом документ автоматически превратится в правильно составленное и отформатированное письмо.

Автоматизация работы с имеющимися данными 161 Ïîñòàíîâêà çàäà÷è Но прежде чем перейти к работе с собственно письмом, необходимо обеспечить небольшой сервис. Дело в том, что в процессе просмотра базы данных пользователь, возможно, создаст несколько писем. А может быть, ему не потребуется создать ни одного письма. Было бы неразумным создавать для каждого письма отдельное приложение Word, вполне достаточно было бы создавать новый документ в одном и том же окне Word. С другой стороны, нельзя запустить Word в начале выполнения функции, в надежде, что работа ему обязательно найдется, — возможно, создавать письма не потребуется. Поэтому здесь необходима переменная-флажок (назовем ее WordRunning) логического типа Boolean, которая будет сигнализировать о факте запуска приложения Word. Если объект WD еще не создавался (WordRunning=False), то, при необходимости создать письмо следует его создать, присвоив затем WordRunning значение True. Если же окажется, что WordRunning=True, значит, создавать объект WD не требуется и достаточно лишь добавить новый документ в его семейство Documents. Dim WordRunning As Boolean ... WordRunning = False ... If Not WordRunning Then Set WD = CreateObject("Word.Application") WD.Visible = True WordRunning = True End If В самом конце функции Анализ_Баланса, переменная WordRunning понадобится нам еще один раз: On Error Resume Next If WordRunning Then WD.Quit Если окно Word в процессе анализа открывалось, то будет предпринята попытка его закрыть. Пользователь получит запрос на сохранение документа при закрытии и будет иметь возможность щелкнуть на кнопке Îòìåíà. Если Word запускался, но уже закрыт пользователем, то возникнет ошибка выполнения, которая будет проигнорирована благодаря оператору On Error Resume Next. Àâòîìàòè÷åñêàÿ ãåíåðàöèÿ ïèñüìà êëèåíòó Итак, переходим непосредственно к автоматической генерации письма клиенту. Вначале создадим текст собственно письма, который присвоим в качестве значения строковой переменной LetterText, предварительно объявив ее, конечно, как String: LetterText = _ "Считаем необходимым обратить Ваше внимание на то," + _ "что Ваш баланс в операциях с нашей компанией " + _ "принял отрицательное значение и составляет " + _ Str(SumIn - SumOut) + " руб." Эта конструкция, при помощи функции Str, встраивает в текст значение отрицательного баланса по текущему клиенту таблицы “Клиенты”. Для удобства выполнения действий с многочисленными свойствами объекта типа LetterContent, следует воспользоваться оператором With…End With: With MyLetter

162 Глава 6. Access: автоматизация офисной базы данных Свойство DateFormat отвечает за дату письма. При помощи функции Date$ присвоим ему текущую системную дату: .DateFormat = Date$ Включим отображение верхнего и нижнего колонтитулов: .IncludeHeaderFooter = True Выберем шаблон письма из папки шаблонов, пусть это будет Ñîâðåìåííîå ïèñüìî (разумеется, местонахождение папки шаблонов Office — в данном случае это папка Templates — может меняться от компьютера к компьютеру): .PageDesign = _ "D:\Office\Templates\1049\Современное письмо ltr.dot" Выберем стиль письма при помощи свойства LetterStyle: .LetterStyle = wdModifiedBlock Чтобы задать имя получателя, нужно обратиться к текущей записи таблицы “Клиенты” и “склеить” значение свойства RecipientName из полей “Имя”, “Отчество” и “Фамилия”: .RecipientName = _ Klients.Fields("Имя") + _ " " + Klients.Fields("Отчество") + _ " " + Klients.Fields("Фамилия") Адрес получателя (свойство RecipientAddress) получить гораздо проще, — для него в таблице отведено специальное поле: .RecipientAddress = Klients.Fields("Адрес") В качестве “инструкций по пересылке” (свойство MailingInstructions) укажем “ДЕЛОВАЯ КОРРЕСПОНДЕНЦИЯ”: .MailingInstructions = "ДЕЛОВАЯ КОРРЕСПОНДЕНЦИЯ" Далее нужно позаботиться о метке (свойство AttentionLine): “Внимание”: .AttentionLine = "Внимание!" Тема письма задается свойством Subject: .Subject = "Отрицательный баланс" В качестве обратного адреса (свойство ReturnAddress) укажем адрес фирмы-отправителя: .ReturnAddress = "ООО Универсал, малая Ивановская 1" Имя отправителя — вероятно, это должно быть имя ответственного работника фирмы: SenderName = "Петров П.П." Строка “уважений и наилучших пожеланий” определяется свойством Closing: .Closing = "С уважением," Наименование организации:

Автоматизация работы с имеющимися данными 163 .SenderCompany = "ООО Универсал" Наконец, наименование должности отправителя: .SenderJobTitle = "Менеджер по работе с клиентами" На этом заполнение свойств объекта LetterContent можно завершить: End With Поскольку предполагается воспользоваться методом SetLetterContent, заполнение всех свойств необязательно (остался целые ряд свойств, которые не были упомянуты). При других способах создания и использования объекта LetterContent необходимо задать значения абсолютно всем свойствам объекта. Итак, поскольку приложение Word уже запущено и доступно через переменную WD, можно создать новый документ, WD.Documents.Add вставить в пустой документ заготовленную строку текста письма, WD.ActiveDocument.Content.InsertBefore LetterText и обратиться к методу SetLetterContent активного документа, передав ему в качестве параметра подготовленный объект типа LetterContent: WD.ActiveDocument.SetLetterContent MyLetter В результате документ превратится в письмо, которое мог бы создать мастер писем, основываясь на шаблоне Ñîâðåìåííîå ïèñüìî. Помошник Office, возможно, предложит создать также конверт или наклейку.

Ïðîãðàììíûé êîä ôóíêöèè Àíàëèç_áàëàíñà Теперь можно свести весь разработанный код воедино и записать исходный текст функции Анализ_баланса (листинг 6.2). ËÈÑÒÈÍÃ 6.2

Public Function Анализ_Баланса() Dim MyDb As Database Dim Klients As Recordset Dim Operations As Recordset Dim WordRunning As Boolean Dim KodKlienta As String Dim SQLstr As String Dim SumIn, SumOut As Currency Dim LetterText As String Dim WD As Word.Application Dim MyLetter As New LetterContent Set MyDb = CurrentDb Set Klients = MyDb.OpenRecordset("Клиенты")

164 Глава 6. Access: автоматизация офисной базы данных

WordRunning = False Klients.MoveFirst Do While Not Klients.EOF If Klients.Fields("Фамилия") "" Then KodKlienta = Klients.Fields("КодКлиента") SQLstr = "SELECT * _ FROM Операции WHERE КодКлиента=" + KodKlienta Set Operations = MyDb.OpenRecordset(SQLstr) SumIn = 0 SumOut = 0 Operations.MoveFirst Do While Not Operations.EOF If Operations.Fields("КодОперации") = 2 Then SumIn = SumIn + _ Operations.Fields("СуммаОперации") Else SumOut = SumOut + _ Operations.Fields("СуммаОперации") End If Operations.MoveNext Loop Operations.Close Set Operations = Nothing If SumOut > SumIn Then If MsgBox("Отрицательное сальдо по клиенту " _ + Klients.Fields("Фамилия") + _ ". Создать письмо?", vbYesNo, _ "Отрицательное сальдо") = vbYes _ Then If Not WordRunning Then Set WD = CreateObject("Word.Application") WD.Visible = True WordRunning = True End If LetterText = _ "Считаем необходимым обратить " + _ "Ваше внимание на то, что Ваш " + _ "баланс в операциях с нашей " + _ "компанией принял отрицательное " + _ "значение и составляет " + _ Str(SumIn - SumOut) + " руб."

Автоматизация работы с имеющимися данными 165 With MyLetter .DateFormat = Date$ .IncludeHeaderFooter = True .PageDesign = _ "D:\Office\Templates\1049\" + _ "Современное письмо ltr.dot" .LetterStyle = wdModifiedBlock .RecipientName = Klients.Fields("Имя") + _ " " + Klients.Fields("Отчество") + _ " " + Klients.Fields("Фамилия") .RecipientAddress = _ Klients.Fields("Адрес") .MailingInstructions = _ "ДЕЛОВАЯ КОРРЕСПОНДЕНЦИЯ" .AttentionLine = "Внимание!" .Subject = "Отрицательный баланс" .ReturnAddress = _ "ООО Универсал, малая Ивановская 1" .SenderName = "Петров П.П." .Closing = "С уважением," .SenderCompany = "ООО Универсал" .SenderJobTitle = _ "Менеджер по работе с клиентами" End With WD.Documents.Add WD.ActiveDocument.Content.InsertBefore LetterText WD.ActiveDocument.SetLetterContent MyLetter End If End If End If Klients.MoveNext Loop Klients.Close Set Klients = Nothing Set Operations = Nothing Set MyDb = Nothing On Error Resume Next If WordRunning Then WD.Quit End Function Чтобы выполнить функцию, достаточно выбрать двойным щелчком макрос Анализ баланса на вкладке Ìàêðîñû. Далее, если в процессе анализа обнаружится отрицательное сальдо по одному из клиентов, достаточно будет щелкнуть на кнопке Äà в окне сообщения, и в открывшемся окне Word будет создано и отформатировано “современное письмо” с заданным содержанием и реквизитами (рис. 6.7).

166 Глава 6. Access: автоматизация офисной базы данных

Рис. 6.7. Письмо сформировано методом SetLetterContent

Глава 7 Â ìèðå îáúåêòîâ MS Office Кое-что об объектах MS Office уже было рассмотрено в предыдущих главах. Чтобы более полно осознать возможности макросов VBA и представить себе, какие вообще задачи можно было бы решать с их помощью, а также узнать, как выполнить ту или иную операцию над ними, посвятим отдельную главу книги краткому обзору объектов MS Office. Направленность и объем этой книги не предполагают полного и детального рассмотрения объектной модели MS Office — этого, собственно, и не требуется никому, кроме профессиональных офис-программистов. Поэтому для начала вполне достаточно познакомиться с основными объектами, которые требуются для решения часто встречающихся задач, и научиться выполнять с ними типичные операции. Более того: многие действия с объектами MS Office можно выполнить десятком способов. Здесь будут рассмотрены на примерах одно-два самых простых действий. Таким образом, эта глава должна представлять собой что-то вроде краткого путеводителя по миру объектов Office или сборника “готовых рецептов” для непрофессионального программиста, который пытается самостоятельно решать при помощи VBA задачи автоматизации, характерные для своего конкретного офиса.

Ðàáîòà ñ îáúåêòàìè MS Word Объектная модель Word, как таковая, достаточно сложна. Однако все в ней подчинено строгим правилам, которые одинаково применимы ко всем уровням ее иерархической структуры: на уровне приложения, документа, абзаца, слова или символа. По этой причине овладеть основными приемами VBA-программирования для Word не так уж и трудно: достаточно понять несколько основных принципов, и уже никакие, даже совсем незнакомые объекты Word не поставят вас в тупик.

Ðàáîòà ñ îáúåêòàìè Word íà óðîâíå ïðèëîæåíèÿ è äîêóìåíòà Îáúåêò Application Приложение Word в целом представлено объектом Application. Это так называемый глобальный объект и во многих ситуациях его имя можно не упоминать, как нечто само собой разумеющееся (то есть, в большинстве приведенных ниже примеров префикс “Application.” можно было бы опустить). В свойствах этого объекта содержатся открытые окна, документы, всевозможные параметры рабочей среды Word и многое другое. Ñâîéñòâî Caption Например, заголовок окна Word задается свойством Caption этого объекта: Application.Caption = “Мое приложение” Ñâîéñòâà ActiveWindow, ActiveDocument è ActivePrinter В свойствах ActiveWindow, ActiveDocument и ActivePrinter содержатся ссылки на, соответственно, текущее окно, документ и принтер. Следующий код максимизирует текущее окно, сохраняет текущий документ и сообщает пользователю имя текущего принтера:

168 Глава 7. В мире объектов MS Office Application.ActiveWindow.WindowState = wdWindowStateMaximize Application.ActiveDocument.Save MsgBox Application.ActivePrinter Ñåìåéñòâî CommandBars Семейство CommandBars представляет панели меню и панели инструментов Word. Например, включить в окне Word режим “крупные значки” можно при помощи следующего кода: Application.CommandBars.LargeButtons = True Ñâîéñòâà DisplayScrollBars è DisplayStatusBar Многие свойства объекта Application отвечают за внешний вид и поведение окна Word. Например, присваивание логического значения False следующим свойствам приведет к тому, что будет отключено отображение полос прокрутки и строки состояния: Application.DisplayScrollBars = False Application.DisplayStatusBar = False Îáúåêò Options Все параметры и настройки Word, содержащиеся в диалоговом окне Ñåðâèñ | Ïàðàìåòðû, доступны посредством объекта Options. Свойство AllowDragAndDrop: управление режимом перетаскивания текста в режиме Правка Например, флажок èñïîëüçîâàòü ïåðåòàñêèâàíèå òåêñòà на вкладке Ïðàâêà можно установить при помощи присваивания: Application.Options.AllowDragAndDrop = True Свойство SaveInterval: задание интервала автосохранения А следующий код задаст интервал автосохранения (см. вкладку Ñîõðàíåíèå) равным десяти минутам: Application.Options.SaveInterval = 10 Ñâîéñòâî StatusBar Свойство StatusBar позволяет записать произвольный текст в строку состояния Word: Application.StatusBar = "Выполняется макрос, ждите..." Ìåòîä Quit Метод Quit завершает работу Word и закрывает его окно. Закрыть окно Word с сохранением всех открытых документов можно при помощи следующего кода: Application.Quit wdSaveChanges Ñåìåéñòâî Documents Семейство Documents, содержащееся в одноименном свойстве объекта Application, представляет все открытые документы. Создать новый или открыть существующий документ можно при помощи методов этого семейства.

Работа с объектами MS Word 169 Метод Add Метод Add создает новый документ на основе указанного шаблона (если не задавать этот параметр, то документ будет создан на основе шаблона Îáû÷íûé). Как правило, используют объектную переменную типа Document, чтобы “запомнить” ссылку на вновь созданный документ: Dim MyDoc As Document Set MyDoc = Documents.Add("C:\MSOffice\Templates\My.dot") Метод Open Метод Open используют для открытия уже существующего, то есть записанного ранее на диск документа: Set MyDoc = Documents.Open("C:\Мои документы\MyDoc1.doc") Методы Close и Activate Обращаться к членам семейства Documents можно как по номерам, так и по именам. Например, следующий код закроет документ с именем “Документ1” и сделает активным четвертый из остающихся открытыми документов (если документа с именем “Документ1” среди открытых документов нет, или, если открыто менее четырех документов, то возникнет ошибка выполнения): Documents(“Документ1”).Close Documents(4).Activate Îáúåêòû ActiveDocument è ThisDocument С точки зрения макроса VBA есть два особенных документа из всех документов семейства Documents. Очень часто эти два документа совпадают в одном: ActiveDocument во многих случаях означает то же самое, что и ThisDocument, хотя, вообще говоря, это разные объекты. Объект ActiveDocument указывает на активный, то есть текущий документ, тот документ, чье окно активно и находится на переднем плане. Объект ThisDocument содержит ссылку на тот документ, в котором, собственно, находится выполняемый макрос. Метод PrintOut: печать документа Вывести на печать документ: ActiveDocument.PrintOut Метод SendMail: отправка документа по электронной почте, как вложение Отправить документ по электронной почте, как вложение: Options.SendMailAttach = True ThisDocument.SendMail В последнем случае по электронной почте будет отправлен документ или шаблон, в котором содержится макрос. Если макрос создан при помощи диалогового окна Ìàêðîñû без уточнения местонахождения макроса, то в путь отправится шаблон Normal.dot. При этом автоматически откроется окно MS Exchange с созданным сообщением E-mail, где остается лишь указать адрес. Метод Close Метод Close имеется как у семейства Documents в целом, так и у каждого отдельно взятого Document-объекта. В первом случае он используется для группового закрытия документов, во втором — для закрытия конкретного документа. Закрыть все документы с сохранением по подтверждению пользователя:

170 Глава 7. В мире объектов MS Office Documents.Close wdPromptToSaveChanges Закрыть текущий документ без сохранения: ActiveDocument.Close wdDoNotSaveChanges Методы Save и SaveAs Явным образом сохранить документ можно при помощи метода Save (сохранение под текущим именем) или SaveAs (сохранить как): ActiveDocument.Save ActiveDocument.SaveAs "C:\Мои документы\MyDoc2.doc"

Îáúåêò Range: ðàáîòà ñ òåêñòîâûì ñîäåðæèìûì äîêóìåíòà Основным рабочим инструментом для операций с содержимым документа Word является объект типа Range. Этот специальный, “абстрактный” объект представляет собой диапазон текста. По названию, а также по своему смыслу он схож с объектом Range рабочего листа Excel. Эти два разных Range-объекта вполне могут соседствовать и даже присутствовать вместе в одной строке кода, как можно было убедиться в одной из предыдущих глав. ← см. в гл. раздел “”.

При работе с текстовым содержимым документа в целом, а также при операциях с абзацами, предложениями, словами и символами, всегда используется один и тот же объект — Range. В каждом случае, в любом контексте он представляет соответствующий текстовый диапазон, будь то весь документ или единственное слово. По этой причине очень важно освоить приемы работы именно с Range-объектом, как таковым, а детали обращения с предложениями, словами и другими разновидностями текстовых диапазонов не будут представлять никакой трудности. Явно задать текстовый диапазон Range можно задать множеством различных способов. Многие свойства документа, не содержащие в своем названии слова “Range”, на самом деле возвращают при обращении к ним объект типа Range. У многих объектов существует свойство Range и также существует метод Range, — в любом случае в результате будет получен Range-объект. Итак, проиллюстрируем одновременно различные способы определения текстового диапазона Range и действия, которые можно проделать с уже определенным Range-диапазоном. Ñâîéñòâî Bold: âûäåëåíèå äèàïàçîíà ïîëóæèðíûì íà÷åðòàíèåì åãî ñèìâîëîâ Выделить полужирным начертанием первые десять символов документа: ActiveDocument.Range(Start:=0, End:=9).Bold = True Ñâîéñòâî InsertBefore: âñòàâêà ñòðîêè Вставить вначале документа строку “АБВГД”: ActiveDocument.Range(Start:=0, End:=0).InsertBefore "АБВГД" Ïðîâåðêà ïðàâîïèñàíèÿ Выполнить проверку правописания в первом разделе документа, и, в случае обнаружения ошибки, выдать сообщение: If Not CheckSpelling(ActiveDocument.Sections(1).Range) Then MsgBox "Обнаружена ошибка!" End If

Работа с объектами MS Word 171 Çàäàíèå âåðõíåãî êîëîíòèòóëà Задать верхний колонтитул первой страницы для второго раздела: ActiveDocument.Sections(2).Headers(wdHeaderFooterFirstPage).Range = _ "Глава 2"

Ñåìåéñòâà Paragraphs, Sentences, Words è Characters Семейство Paragraphs представляет все абзацы документа, семейство Sentences — все предложения, в семействе Words содержатся слова, а в семействе Characters — символы документа. Все эти семейства устроены одинаковым образом. Ñâîéñòâà First è Last К их членам можно обращаться по номерам, свойства First и Last указывают на, соответственно, первый и последний элементы. Ñâîéñòâà Count Свойстве Count содержится число элементов в семействе. И для всех этих семейств в любом случае в результате возвращается объект типа Range. Ñâîéñòâî Alignment Задать выравнивание по правому краю для первого абзаца: ActiveDocument.Paragraphs(1).Alignment = wdAlignParagraphRight Ñîçäàíèå âîêðóã àáçàöà ðàìêó çàäàííîãî ñòèëÿ Заключить первый абзац в рамку стиля 3D: ActiveDocument.Paragraphs.First.Borders.OutsideLineStyle = _ wdLineSyleEmboss3D Âñòàâêà òåêñòà ïîñëå çàäàííîãî àáçàöà Вставить текст после последнего абзаца: ActiveDocument.Paragraphs.Last.Range.InsertAfter "Конец." Èçìåíåíèå ôîíà àáçàöà Задать для пятого абзаца штриховку (узор) 15%: ActiveDocument.Paragraphs(5).Shading.Texture = _ wdTexture15Percent Èçìåíåíèå ñòèëÿ àáçàöà Задать стиль 1-го абзаца Çàãîëîâîê1: ActiveDocument.Paragraphs.First.Style = "Заголовок 1" Èçìåíåíèå ñòèëÿ àáçàöà Аналогичным образом можно обращаться к предложениям документа. Выбрать для первого предложения шрифт Arial: ActiveDocument.Sentences(1).Font.Name = "Arial"

172 Глава 7. В мире объектов MS Office Ïåðåìåùåíèå ïî òåêñòó Задать смещение вверх на три пункта для символов первого предложения и вниз на три пункта для второго: ActiveDocument.Sentences(1).Font.Position = 3 ActiveDocument.Sentences(2).Font.Position = - 3 Ìåòîä Delete Удалить последнее предложение: ActiveDocument.Sentences.Last.Delete Ïðåîáðàçîâàíèå ñèìâîëîâ â âåðõíèé ðåãèñòð Преобразовать символы первого предложения в верхний регистр: ActiveDocument.Sentences(1).Case = wdUpperCase Изменение начертания символов Выделить первое слово полужирным начертанием: ActiveDocument.Words.First.Bold = True Ñåìåéñòâî StoryRanges Семейство StoryRanges также возвращает Range-объект, — оно обеспечивает доступ к различным структурным компонентам текста, таким, как основной текст, примечания, сноски, колонтитулы и др. Выбор нужного компонента осуществляется посредством соответствующей константы. Например, задать шрифт Arial для основного текста документа (не затронув при этом все остальное, как случилось бы при обращении к свойству ActiveDocument.Range), можно при помощи следующего кода: ActiveDocument.StoryRanges(wdMainTextStory).Font.Name = "Arial" Выделить полужирным нижний колонтитул 1-й страницы: ActiveDocument.StoryRanges(wdFirstPageFooterStory).Bold = True

Ðàáîòà ñî ñïèñêàìè è òàáëèöàìè äîêóìåíòà Word Word-òàáëèöû Таблицы в документе Word представлены объектами типа Table, доступными через семейство Tables. Каждая таблица — объект Table — состоит из строк (объекты типа Row, объединенные в семейство Rows) и столбцов (объекты типа Column, объединенные в семейство Rows). Кроме того, таблицу можно рассматривать, как набор ячеек (объектов типа Cell, объединенных в семейство Cells). Óïðàâëåíèå ãðàíèöàìè òàáëèö Разрешить отображение границ для первой таблицы документа: ActiveDocument.Tables(1).Borders.Enable = True Óäàëåíèå ñòîëáöà òàáëèöû Удалить первый столбец в первой таблице:

Работа с объектами MS Word 173 ActiveDocument.Tables(1).Columns(1).Delete Èçìåíåíèå ôîíà òàáëèöû Выделить первую строку первой таблицы штриховкой 15%: ActiveDocument.Tables(1).Rows(1).Shading.Texture = _ wdTexture15Percent Ñîçäàíèå òàáëèöû Создать новую таблицу с заданными параметрами и заменить ею фрагмент, определенный выделенной в документе областью (объект Selection): ActiveDocument.Tables.Add Selection.Range, 4, 3 Àâòîôîðìàò òàáëèöû Отформатировать таблицу с применением автоформата Ñïèñîê1: ActiveDocument.Tables(1).AutoFormat wdTableFormatList1 Çàïèñü â ÿ÷åéêè òàáëèöû äàííûõ К ячейкам таблицы можно обращаться по номеру строки и столбца, при этом содержимое ячейки представлено Range-объектом. Например, записать в ячейку первого столбца и первой строки строку “Первая ячейка” можно при помощи следующего кода: ActiveDocument.Tables(1).Cell(1, 1).Range = "Первая ячейка" Èçìåíåíèå øèðèíû ñòîëáöîâ òàáëèöû Задать для всех столбцов первой таблицы ширину, равную 33 пунктам: For N = 1 To ActiveDocument.Tables(1).Columns.Count ActiveDocument.Tables(1).Columns(N).Width = 33 Next N Выровнять все столбцы по ширине: ActiveDocument.Tables(1).Columns.DistributeWidth Âûðàâíèâàíèå òåêñòà â òàáëèöå Задать для первой строки выравнивание текста по центру: ActiveDocument.Tables(1).Rows(1).Alignment = wdAlignRowCenter Äîáàâëåíèå çàãîëîâêà òàáëèöû Сделать первую строку заглавной: ActiveDocument.Tables(1).Rows(1).HeadingFormat = True Àâòîñóììèðîâàíèå ÿ÷ååê òàáëèöû Вставить в ячейку (2, 3) поле автосуммирования, которое будет отображать сумму значений в ячейках выше или левее: ActiveDocument.Tables(1).Cell(2, 3).AutoSum

174 Глава 7. В мире объектов MS Office Ðàçáèåíèå ÿ÷ååê òàáëèöû Разбить ячейку (2, 3) на 4 ячейки: ActiveDocument.Tables(1).Cell(2, 3).Split 2, 2

Word-ñïèñêè Списки в документе Word устроены несколько иным образом. Такого объекта, как список, не существует. Абзацы, составляющие список, являются обычными абзацами документа, и когда речь идет о списке, то имеется в виду форматирование ряда абзацев. Îáúåêò ListFormat Форматирование для каждого абзаца списка определяется объектом ListFormat, который доступен в качестве свойства текстового диапазона. Чтобы отформатировать некоторый фрагмент текста, как список, необходимо, прежде всего, определить Range-объект, заключающий в себе этот диапазон. Например, для того чтобы отформатировать как список абзацы с 5-го по 10-й, можно создать переменную MyRange типа Range, которой следует присвоить в качестве значения необходимый диапазон — диапазон с начала 5-го абзаца по конец 10-го: Dim MyRange As Range ... Set MyRange = ActiveDocument.Range( _ Start:= ActiveDocument.Paragraphs(5).Range.Start, _ End:= ActiveDocument.Paragraphs(10).Range.End) Затем можно будет воспользоваться свойством ListFormat заданного при помощи переменной MyRange диапазона, чтобы отформатировать абзацы 5-10, как маркированный список: MyRange.ListFormat.ApplyListTemplate _ ListTemplate:=ListGalleries(wdBulletGallery) _ .ListTemplates(1) Обратная задача, то есть обращение к списку с целью получить входящие в него абзацы документа, решается при помощи свойства ListParagraphs. Например, выделить полужирным параграф, являющийся первым элементом второго списка документа, можно при помощи оператора: ActiveDocument.Lists(2).ListParagraphs(1).Range.Bold = True

Ðàáîòà ñ îáúåêòàìè MS Excel С точки зрения VBA-программирования, Excel занимает особенное место среди всех приложений MS Office. Дело в том, что вообще язык Visual Basic для приложений, как таковой, был первоначально задуман и создан именно для Excel. Позднее сфера действия VBA была распространена и на другие приложения Office, однако “родной” для VBA остается все же рабочая среда Excel. Из этого обстоятельства можно сделать два вывода. Первый вывод заключается в том, что объектная модель Excel отличается наибольшей зрелостью и совершенством уже просто в силу своего почтенного возраста. Второй вывод: именно у пользователей Excel в первую очередь возникла потребность в макроязыке, при помощи которого можно было бы автоматизировать свою работу, и поэтому, с объективной точки зрения, Excel и есть то самое приложение, где макросы VBA нужнее всего. В самом деле, Excel и VBA — очень гармоничная пара, гармоничная в самом глубоком смысле. Подобно тому, как VBA не является “настоящим” языком программирования (ведь сила VBA — не в широте его возможностей, а в простоте и непритязательности — в среде Office он повсюду, и повсюду,

Работа с объектами MS Excel 175 в любой ситуации, можно опереться на его программный код), электронная таблица Excel не являются “настоящей” базой данных, и сила Excel в том, что он обеспечивает, пусть не академически строгую и сверхмощную, как в Access, однако живую работу с табличными данными, где все делается запросто и все происходит на глазах у пользователя.

Ðàáîòà ñ îáúåêòàìè Excel íà óðîâíå ïðèëîæåíèÿ, ðàáî÷åé êíèãè è ëèñòà Îáúåêò Application Объект Application, как это было и в случае с Word, представляет приложение Excel в целом. Свойство Caption определяет заголовок окна приложения: Application.Caption = "Обзор цен за 2000 год" Ñâîéñòâà Workbooks, ActiveWorkbook è ThisWorkbook В свойстве Workbooks содержится семейство открытых рабочих книг, а в свойстве Worksheets — семейство рабочих листов, но рабочих листов не всех открытых книг, а только активной книги. Соответственно, свойство ActiveWorkbook указывает на активную рабочую книгу. А вот свойства ActiveWorksheet не существует, — вместо него используют свойство ActiveSheet, которое указывает на активный лист активной книги (все листы книги — не только рабочие листы, но и листы диаграмм, содержатся в семействе Sheets). Свойство ThisWorkbook указывает на ту рабочую книгу, в которой, собственно, содержится выполняемый в данный момент времени макрос. Application — глобальный объект и по этой причине во многих случаях подразумевается именно он. В приведенных ниже примерах зачастую префикс “Application.” можно было бы опустить. Метод SaveAs Сохранить текущую (активную) рабочую книгу в файле с заданным именем: Application.ActiveWorkbook.SaveAs "MyWorkBook" Метод Activate Сделать активной вторую из открытых книг: Application.Workbooks(2).Activate Метод Close Закрыть книгу с именем MyBook без сохранения изменений: Application.Workbooks(“MyBook”).Close SaveChanges:=False Метод Open Открыть книгу, хранящуюся в файле с заданным именем: Application.Workbooks.Open “C:\MyBook.xls” Ñâîéñòâî ActiveSheet Свойство ActiveSheet объекта Application возвращает активный лист активной книги. Если ни один из листов не активен, свойство вернет значение Nothing. При обращении к этому свойству необходимо учитывать два обстоятельства. Первое: одноименным свойством обладает

176 Глава 7. В мире объектов MS Office объект типа Worksheet. Второе обстоятельство заключается в том, что под именем ActiveSheet может скрываться не рабочий лист, а лист диаграммы — в этом случае свойство вернет объект иного типа, чем Worksheet и макрос, обращающийся к свойствам рабочего листа, “потерпит неудачу“. Свойство Name Изменить имя текущего листа: Application.ActiveSheet.Name = "Январь" Ñâîéñòâî DisplayScrollBars: ïðèìåð ñîêðûòèÿ ïîëîñ ïðîêðóòêè Многие свойства объекта Application отвечают за внешний вид и поведение окна приложения, за различные параметры рабочей среды Excel. Например, следующий код запрещает отображение полос прокрутки и строки состояния: Application.DisplayScrollBars = False Application.DisplayStatusBar = False Ñâîéñòâî StatusBar Свойство StatusBar отвечает за текст строки состояния в окне Excel: Application.StatusBar = "Работает макрос, ждите..." Óâåëè÷èòü ìàñøòàá àêòèâíîãî îêíà Увеличить масштаб активного окна на 50 % можно при помощи следующего оператора: Application.ActiveWindow.Zoom = 150 Ñâîéñòâî FixedDecimal Включить режим “фиксированный десятичный формат при вводе” и задать число десятичных разрядов, равным 2: Application.FixedDecimal = True Application.FixedDecimalPlaces = 2 Ìåòîä Calculate Выполнить принудительный пересчет всех рабочих листов всех открытых книг: Application.Calculate Ñåìåéñòâî Sheets Семейство Sheets заключает в себе все листы активной рабочей книги. Обращаться к элементам семейства можно по номерам или именам. Метод Add При помощи метода Add можно создавать новые листы. Например, вставить в книгу 12 новых листов и присвоить им имена, соответствующие названиям 12 месяцев года, можно при помощи следующего кода: ActiveWorkbook.Sheets.Add Count:=12 For N = 1 To 12 ActiveWorkbook.Sheets(N).Name = _

Работа с объектами MS Excel 177 Format(DateSerial(1, N, 1), "mmmm") Next N Вставка листа диаграммы По умолчанию в семейство Sheets добавляются рабочие листы. Однако, если указать тип вставляемого листа явным образом, то можно вставить лист другого типа. Например, вставить лист диаграммы перед последним листом рабочей книги можно посредством оператора: ActiveWorkbook.Sheets.Add _ Before:=Worksheets(Worksheets.Count) Type:=xlChart В свойстве Count любого семейства, как уже указывалось прежде, содержится число элементов семейства. Метод Delete Удалить лист можно при помощи метода Delete: Worksheets("Обзор1998").Delete Свойство Visible Скрыть лист можно при помощи свойства Visible: Worksheets("Обзор1999").Visible = False Метод Move Метод Move перемещает лист в заданную позицию в книге. Переместить лист “Обзор2000” в конец книги: Worksheets("Обзор").Move , Worksheets(Worksheets.Count) Метод Copy Метод Copy копирует лист в заданную позицию в книге. Скопировать лист “Бланк” в позицию после листа “Обзор2000”: Worksheets("Бланк").Copy , Worksheets("Обзор2000") Ìåòîä Quit Метод Quit завершает работу приложения Excel: Application.Quit

Ðàáîòà ñ ñîäåðæèìûì ðàáî÷åãî ëèñòà Îáúåêò Range Основным рабочим инструментом для операций с содержимым рабочего листа при помощи VBA является объект Range. Объект такого типа возвращают как одиночные ячейки листа, так и диапазоны ячеек. Çàäàíèå äèàïàçîíà Задать диапазон Range можно различными способами. Например, выражение

178 Глава 7. В мире объектов MS Office ActiveSheet.Range("A3") вернет ячейку A3 текущего рабочего листа. Если ячейка A3 активна, то есть выбрана, то тот же самый Range-объект вернет выражение ActiveCell Ñâîéñòâî Offset Свойство Offset позволяет задавать относительное смещение и также возвращает Rangeобъект. Выделить ячейку на три столбца правее текущей: ActiveCell.Offset(0, 3).Select Прямоугольный диапазон задается точно так же, как и в формулах рабочего листа: ActiveSheet.Range("A3:B5") Если на листе определены именованные диапазоны, то обратиться к такому диапазону можно при помощи выражения: ActiveSheet.Range("ИмяДиапазона") Объект типа Range на рабочем листе Excel многолик и вездесущ — весь текущий рабочий лист ActiveSheet представлен Range-объектом, диапазон на нем ActiveSheet.Range("ИмяДиапазона") — это тоже Range-объект, и некоторая ячейка этого диапазона ActiveSheet.Range("ИмяДиапазона").Cells(N, M) — это тоже Rangeобъект! Ñâîéñòâî Cells Свойством (семейством) Cells обладают все Range-объекты. По умолчанию Cells(M, N) означает ячейку в M-й строке и N-м столбце рабочего листа, но точно таким же образом можно обратиться к ячейкам любого диапазона. Например, выражение ActiveSheet.Range("A3:B5").Cells(2, 2) вернет ячейку B4. Очень часто требуется указывать диапазоны в циклических конструкциях типа For. Чтобы можно было при задании диапазона пользоваться номерами строк и столбцов, Range-объект можно определить указанием угловых ячеек диапазона через семейство Cells рабочего листа. Выражение ActiveSheet.Range(Cells(1, 2), Cells(3, 4)) задает диапазон ячеек “B1:E3”. Ñâîéñòâî EntireColumn Свойство Range-объекта EntireColumn возвращает Range-объект, представляющий весь столбец, к которому принадлежит указанный диапазон. Например, следующая строка кода удаляет весь текущий столбец: ActiveCell.EntireColumn.Delete Ñâîéñòâî EntireRow Аналогичную функцию выполняет свойство EntireRow в отношении строк. Например, следующая строка кода удаляет всю строку:

Работа с объектами MS Excel 179 ActiveCell.EntireRow.Delete Ñâîéñòâî UsedRange Свойство UsedRange возвращает Range-объект, представляющий использованный диапазон ячеек на листе. Например, следующая строка кода сообщает адрес использованной области: MsgBox ActiveSheet.UsedRange.Address Óäàëåíèå äèàïàçîíà ñî ñäâèãîì îñòàâøèõñÿ ÿ÷ååê âëåâî При обращении к методу Delete можно указать при помощи специальных констант направление сдвига остающихся ячеек. Удалить диапазон “A3:D6” со сдвигом оставшихся ячеек влево: Range("A3:D6").Delete xlShiftToLeft Ìåòîä Insert: ïðèìåð âñòàâêè äèàïàçîíà ñî ñäâèãîì îñòàâøèõñÿ ÿ÷ååê âïðàâî Аналогичным образом указывается направление сдвига при вставке ячеек. Вставить диапазон со сдвигом ячеек вправо: Range("A3:D6").Insert xlShiftToRight Äîñòóï ê ñîäåðæèìîìó ÿ÷ååê è äèàïàçîíîâ Доступ к содержимому ячеек и диапазонов, то есть к содержащимся в них формулам, ссылкам и константам, а также форматирование ячеек и диапазонов осуществляется посредством свойств объекта Range. Запись в ячееку значения Записать значение 15 в ячейку A3: ActiveSheet.Range("A3") = 15 Ñâîéñòâî Formula Свойство Formula позволяет поместить в ячейку формулу: Range("A11").Formula = "=SUM(A1:A10)” Ñâîéñòâî FormulaLocal При работе с формулами посредством кода VBA необходимо учитывать одно обстоятельство. Например, в русской версии Office присваивание Range("A11").Formula = "=СУММ(A1:A10)” приведет к тому, что в ячейке A11 будет отображаться ошибка, хотя ручной ввод в ячейку той же самой формулы привел бы к успеху. Дело в том, что свойство Formula предназначено для хранения оригинальной, нелокализованной формулы. Если в локализованной версии Excel необходимо использовать версию формулы на национальном языке, то помещать ее следует в свойство FormulaLocal: Range("A11").FormulaLocal = "=СУММ(A1:A10)” При работе в окне Excel рабочий лист “сам разбирается”, какую версию формулы вводит пользователь и куда ее поместить, но при обращении к ячейке из кода VBA необходимо отличать оригинальные имена функций от локальных.

180 Глава 7. В мире объектов MS Office Ñòèëü ññûëîê R1C1 Свойство FormulaR1C1 (а также его локальный аналог FormulaR1C1Local) позволяет записать формулу со стилем ссылок R1C1: ActiveSheet.Range("B1").FormulaR1C1 = "=SQRT(R1C1)" Ñâîéñòâî FormulaHidden Если включена защита листа, то при помощи свойства FormulaHidden можно скрыть формулы. Например, следующая строка кода позволяет скрыть формулы в столбце: Worksheets("Sheet1").Columns("A").FormulaHidden = True Ñâîéñòâî Value Свойством по умолчанию (то есть, свойством, имя которого можно во многих случаях не упоминать) для Range-объекта является свойство Value. Здесь содержится значение ячейки, если в ячейку записана константа. Если в ячейке содержится формула, то свойство Value вернет результат вычисления по формуле. Например, следующая строка кода выводит на экран окно со значением из ячейки A3: MsgBox Range("A3").Value Ìåòîä AutoFill: àâòîçàïîëíåíèå äèàïàçîíà Метод AutoFill позволяет применить к диапазону автозаполнение. В качестве параметров необходимо указать границы заполнения и константу, определяющую вид заполнения. Например, следующий код заполнит диапазон A8:A19 названиями месяцев года: Range("A8") = "Январь" Range("A8").AutoFill Range("A8:A19"), xlFillMonths Ôîðìàòèðîâàíèå ÿ÷ååê Форматирование ячеек также осуществляется посредством свойств соответствующих Rangeобъектов. Например, снабдить ячейку A3 линиями можно при помощи оператора: Range("A3").Borders.LineStyle = xlContinuous Ñâîéñòâî ColumnWidth Свойство ColumnWidth определяет ширину столбца. Единицей измерения служит ширина символов стиля Îáû÷íûé. Если речь идет о пропорциональных шрифтах, то имеется в виду ширина символа равная нулю. Таким образом, последовательность "0123456789" идеально поместится в ячейку шириной 10. Следующая строка кода задает для текущего столбца ширину, равную 10 символам: ActiveCell.ColumnWidth = 10 Ñâîéñòâî RowHeight Схожее по смыслу свойство RowHeight отвечает за высоту строки, однако в качестве единицы измерения оно использует типографские пункты. Ñâîéñòâî Font Свойство Font содержит одноименный объект, свойства которого позволяют задавать параметры шрифта для того диапазона, о чьем свойстве Font идет речь.

Работа с объектами MS Excel 181 Отформатировать шрифт в диапазоне можно, например, следующим образом: With Range("A3:D9").Font .Bold = True .Shadow = True .Name = "Arial" .Size = 16 End With Свойства Bold, Shadow, Name и Size Свойства Bold и Shadow определяют, соответственно, признак начертания “жирный” и эффект “с тенью”, а свойства Name и Size задают имя и размер шрифта. Îáúåêò Interior Объект Interior, содержащийся в одноименном свойстве Range-объекта, определяет черты “внутреннего” форматирования ячейки, как таковой. Например, задать цвет фона для ячейки и залить ее узором можно при помощи такого кода: With Range("A3:D7").Interior .Color = RGB(192, 164, 12) .Pattern = xlPatternLightDown End With Ñâîéñòâî Locked Свойство Locked разрешает (False) или запрещает (True) модификацию содержимого ячеек в ситуации, когда лист в целом защищен от внесения изменений. Разрешить модификацию ячеек: Range("A3:C9").Locked = False Ñâîéñòâî Style Свойство Style определяет стиль, заданный для ячеек диапазона. Например: Range("A3").Style = "Процентный" Î÷èñòêà äèàïàçîíà Очистить диапазон можно при помощи одного из следующих методов. Очистить все: Range("A5:A10").Clear Очистить содержимое (не влияет на примечания и форматирование ячеек): Range("A5:A10").ClearContents Очистить форматы (не влияет на содержимое): Range("A5:A10").ClearFormats Îáúåêò Characters Чтобы отформатировать отдельные символы в ячейке, следует использовать объект Characters, содержащийся в одноименном свойстве Range-объекта. Для этого надо указать позицию начального символа и число символов, которые надо отформатировать. Выделить жирным 3-й и 4-й символы в ячейке A3:

182 Глава 7. В мире объектов MS Office Range("A3").Characters(3, 2).Font.Bold = True

Ðàáîòà ñ îáúåêòàìè MS Access Работа с базами данных Access при помощи VBA — непростое занятие. Собственно объектная модель Access несложна. Она включает в себя всего несколько объектов, обеспечивающих доступ к макросам, модулям VBA, элементам пользовательского интерфейса и среде разработки. Доступ же непосредственно к данным Access осуществляется посредством специальных объектов, работе с которыми посвящены целые книги. Не будем пытаться “объять необъятное”, а ограничимся рядом простых “рецептов”, которые достаточны для выполнения несложных типовых операций с данными Access.

Ðàáîòà ñî ñòðóêòóðîé áàçû äàííûõ Access Îáúåêòû Database (áàçà äàííûõ), TableDef (îïðåäåëåíèå òàáëèöû) è ñåìåéñòâî Field (ïîëÿ òàáëèöû) База данных в целом представлена объектом типа Database. Объекта “таблица” не существует. Доступ к таблицам осуществляется через посредство объектов TableDef (определение таблицы). Объекты типа Field, объединенные в семейство Field соответствующего объекта TableDef, представляют поля таблицы. Предположим, что нужно изменить при помощи кода VBA (неважно, где он расположен — в программном модуле Access или, например, при условии задания соответствующих ссылок, в рабочей книге Excel) структуру базы данных MyOffice, файл которой расположен в корневом каталоге диска C:. Допустим, нужно добавить в таблицу “Клиенты” еще одно поле — текстовое поле “Примечания” для заметок. Вот какие, в этом случае, нужно было бы выполнить действия. Îáúÿâëåíèå ïåðåìåííûõ òèïà áàçà äàííûõ, îïðåäåëåíèå òàáëèöû è ïîëå òàáëèöû Сначала необходимо объявить переменные: базу данных, определение таблицы и поле таблицы. Dim MyDB As Database, MyTDF As TableDef, MyFLD As Field Ìåòîä OpenDatabase: îòêðûòèå áàçû äàííûõ Затем следует открыть базу данных, содержащуюся в файле C:\MyOffice.mdb: Set MyDB = OpenDatabase("C:\MyOffice.mdb") Поскольку база данных открыта и содержится в переменной MyDB, можно обратиться к семейству определений таблиц и присвоить требуемой значение переменной MyTDF: Set MyTDF = MyDB.TableDefs("Клиенты") Ìåòîä CreateField îáúåêòà TableDef: äîáàâëåíèå òåêñòîâîãî ïîëÿ Затем, при помощи метода CreateField объекта TableDef, можно добавить текстовое поле с заданным именем заданного размера (константа dbText указывает на тип поля): Set MyFLD = MyTDF.CreateField("Примечание", dbText, 200)

Работа с объектами MS Access 183 Ìåòîä Append Но дело еще не сделано — поле создано, существует и содержится в переменной MyFLD. Но в состав таблицы “Клиенты” оно еще не включено. Метод Append позволяет включить поле в таблицу. MyTDF.Fields.Append MyFLD Ìåòîä Close: çàêðûòèå áàçû äàííûõ Остается закрыть базу данных, и освободить выделенную под переменную MyDB память: MyDB.Close Set MyDB = Nothing Если теперь запустить Access и открыть базу данных MyOffice, то нетрудно убедиться, что в таблице (в таблице, но не в форме!) “Клиенты” появилось новое поле. Ìåòîä CreateTable: ñîçäàíèå òàáëèöû Если требуется создать новую таблицу в существующей базе данных, то действовать следует аналогичным образом, только вместо открытия таблицы обращением к семейству TableDefs следует использовать метод CreateTable того же семейства, и, пока на таблицу указывает объектная переменная, создать в ней необходимые поля. Точно так же, как и в случае с полями, созданную таблицу необходимо затем включить в состав семейства при помощи метода Append. Предположим, требуется добавить в офисную базу данных таблицу “Счета” для ведения лицевых счетов клиентов. Каждая запись в таблице должна состоять из двух полей: поля “ЛицевойСчет” (числового типа “длинное целое”) и поля “Остаток” для денежных значений типа Currency. Вот необходимая последовательность действий для ее создания. 1. Объявить необходимые переменные: Dim MyTDF As TableDef Dim MyDB As Database 2.

Открыть базу данных: Set MyDB = OpenDatabase("C:\MyOffice.mdb")

3.

Создать новую таблицу: Set MyTDF = MyDB.CreateTableDef("ЛицевыеСчета")

4.

Создать в новой таблице два поля: With MyTDF .Fields.Append.CreateField("ЛицевойСчет", dbLong) .Fields.Append.CreateField("Остаток", dbCurrency) End With

5.

Включить новую таблицу в базу данных: MyDB.TableDefs.Append MyTDF

6.

Закрыть базу данных и освободить память: MyDB.Close Set MyDB = Nothing

184 Глава 7. В мире объектов MS Office Ñâîéñòâà ïîëåé При создании новых полей можно задавать различные свойства поля. Набор возможностей здесь аналогичен набору возможностей при работе в окне конструктора таблиц. Ââîä â ïîëå òàáëèöû çíà÷åíèÿ ïî óìîë÷àíèþ (DefaultValue) Например, для поля можно задать значение по умолчанию, то есть значение, которое будет в нем содержаться, если пользователь не введет в него никакого значения: Dim MyFLD As Field, MyTDF as TableDef, MyDB As Database … Set MyDB = OpenDatabase("C:\MyOffice.mdb ") Set MyTDF = MyDB.TableDefs("Клиенты") Set MyFLD = MyTDF.Fields("Телефон") MyFLD.DefaultValue = "000-00-00" Ñâîéñòâî Required: ïðîâåðêà äîïóñòèìûõ çíà÷åíèé ââîäèìûõ â ïîëå Свойство Required определяет, допустимы ли для поля пустые значения. Поле нельзя оставлять пустым. Access не позволит создать новую запись, пока поле не будет заполнено: Set MyFLD = MyTDF.Fields("Телефон") MyFLD.Required = True Ñâîéñòâî Type: òèï äàííûõ Тип данных, для хранения которых предназначено поле, задается свойством Type. В это свойство необходимо записать соответствующую константу. Если поле должно быть текстовым, то одновременно следует в свойстве Size указать размер поля: Set MyFLD = MyTDF.Fields("ОписаниеКлиента") MyFLD.Type = dbText MyFLD. Size = 200 Ñâîéñòâî FieldSize: ðàçìåð ïîëÿ Размер поля любого типа содержится в свойстве FieldSize, которое не следует путать со свойством Size. Значение в FieldSize показывает физический размер в байтах, которое поле занимает в базе данных. Êîíñòàíòû òèïà äàííûõ ïîëÿ Полный список констант, определяющих тип данных поля, можно узнать при помощи справочной системы. Далее перечислены некоторые из них: dbCurrency — денежные значения типа Currency; dbDate — значения даты/времени; dbFloat — дробные числа с плавающей точкой; dbInteger — целые числа; dbText — текстовые значения.

Ðàáîòà ñ ñîäåðæèìûì áàçû äàííûõ Access “Взаимоотношения” Access и VBA имеют свою предысторию, которая накладывает отпечаток на принципы работы в VBA-коде с данными Access. Дело в том, что собственные средства автоматизации Access существовали еще до изобретения VBA. Это так называемые макрокоманды Access. Использование этих макрокоманд — и по сей день самый простой способ работать с базой данных Access.

Работа с объектами MS Access 185

Îáúåêò DoCmd Для интеграции механизма макрокоманд в код VBA используется специальный объект DoCmd (выполнить команду), методы которого в точности соответствуют старым макрокомандам Access. Объект DoCmd позволяет оперировать содержимым не только таблиц, но и форм и других объектов базы данных. Ниже приведен ряд примеров, иллюстрирующих работу с методами объекта DoCmd. Ìåòîä OpenTable: îòêðûòèå òàáëèöû Пример, открывающий форму “Клиенты” с отображением записей, в которых поле “Фамилия” содержит значение “Иванов”: DoCmd.OpenTable "Клиенты" DoCmd.ApplyFilter , "Фамилия ='Иванов'" Ìåòîä OutputTo: âûâîä òàáëèöû Пример вывода таблицы “Клиенты” в файл формата HTML: DoCmd.OutputTo acOutputTable, "Клиенты", _ acFormatHTML, "C:\Clients.htm" Ìåòîä PrintOut: ïå÷àòü òàáëèöû Пример, открывающий форму “Клиенты” и печатающий страницы 1-5: DoCmd.OpenTable "Клиенты" DoCmd.PrintOut acPages, 1, 5 Ìåòîä OpenForm: îòêðûòèå ôîðìû Метод Maximize: развернуть форму Пример, открывающий форму “Клиенты” и разворачивающий ее на весь экран: DoCmd.OpenForm "Клиенты" DoCmd.Maximize Метод Maximize: свернуть форму до значка Пример, открывающий форму “Клиенты” и сверачивающий ее в значок на рабочей поверхности окна Access: DoCmd.OpenForm "Клиенты" DoCmd.Minimize Ìåòîä GoToRecord: íàâèãàöèÿ ôîðìû Для навигации формы, то есть для перемещения формы по записям таблицы, используют метод GoToRecord. Пример, открывающий форму “Клиенты” с переходом на 101-ю запись: DoCmd.OpenForm "Клиенты" DoCmd.GoToRecord acDataForm, "Клиенты", acGoTo, 101 Ìåòîä GoToControl: ïåðåäà÷à ôîêóñà ýëåìåíòó óïðàâëåíèÿ ôîðìû Метод GoToControl служит для передачи фокуса одному из элементов управления формы. Открыть форму “Клиенты” и поместить курсор в поле “E-Mail”:

186 Глава 7. В мире объектов MS Office DoCmd.OpenForm "Клиенты" DoCmd.GoToControl "E-Mail" Ñâîéñòâî AllowDeletions: çàïðåò óäàëåíèÿ çàïèñåé ïîëüçîâàòåëåì Пример, открывающий форму “Клиенты” и запрещающий в ней удаление записей пользователем: Dim MyFrm As Form … DoCmd.OpenForm "Клиенты" Set MyFrm = Forms![Клиенты] … MyFrm.AllowDeletions = False Ñâîéñòâî AllowEdits: çàïðåò èçìåíåíèÿ çàïèñåé Пример, запрета внесения изменений в существующие записи при помощи формы “Клиенты”: DoCmd.OpenForm "Клиенты" Set MyFrm = Forms![Клиенты] MyFrm.AllowEdits = True Ñâîéñòâà ôîðìû Семейство Controls Поля, списки и вообще все размещенные на форме элементы управления доступны посредством семейства Controls данной формы. Например, вот как можно открыть форму “Клиенты” и загрузить значение “Иванов” в поле “Фамилия”: DoCmd.OpenForm "Клиенты", acNormal Set MyFrm = Forms![Клиенты] MyFrm.Controls("Фамилия").Value = "Иванов" Свойство CurrentRecord Свойство формы CurrentRecord возвращает номер текущей записи (это свойство доступно только для чтения). Код, позволяющий вывести на экран окно-сообщение с номером текущей записи: DoCmd.OpenForm "Клиенты", acNormal Set MyFrm = Forms![Клиенты] MsgBox "Текущая запись: " + MyFrm.CurrentRecord Свойство Dirty Свойство Dirty сигнализирует о том, что запись содержит несохраненные изменения: DoCmd.OpenForm "Клиенты", acNormal Set MyFrm = Forms![Клиенты] … If MyFrm.Dirty Then MsgBox "Запись изменена. Сохранить?", vbYesNo End If …

Работа с объектами MS Access 187 Свойства FilterOn и Filter Пользуясь свойством Filter можно наложить фильтр на отображаемые формой записи. При этом фильтр необходимо включить, присвоив логическое значение True свойству FilterOn. Отключить фильтр можно присваиванием значения False. Пример, открывающий форму “Все операции” с отображением только тех записей, где сумма операции больше 1000: … DoCmd.OpenForm "Все операции", acNormal Set MyFrm = Forms![Все операции] … MyFrm.Filter = "СуммаОперации > 1000" MyFrm.FilterOn = True … Пример, открывающий форму “Клиенты” с отображением только тех записей, где у клиента есть адрес E-Mail (то есть, поле “E-Mail” не пусто): DoCmd.OpenForm "Клиенты", acNormal Set MyFrm = Forms![Клиенты] … MyFrm.Filter = "[E-Mail] ‘’" MyFrm.FilterOn = True Свойство NavigationButtons Свойство NavigationButtons определяет наличие в окне формы кнопок перехода и номера записи Пример, открывающий форму “Клиенты” без навигационных кнопок: … DoCmd.OpenForm "Клиенты", acNormal Set MyFrm = Forms![Клиенты] … MyFrm.NavigationButtons = False Ñåìåéñòâî Forms Вообще, все открытые формы доступны посредством семейства Forms. Это семейство можно просматривать при помощи цикла For…Each. Пример, скрывающий все открытые формы: Dim MyFrm As Form … For Each MyFrm In Forms MyFrm.Visible = False Next Пример, закрывающий все открытые формы с запросом на сохранение изменений: Dim MyFrm As Form … For Each MyFrm In Forms MyFrm.Close acForm, MyFrm.Name, acSavePrompt Next

188 Глава 7. В мире объектов MS Office Ìåòîä Quit: çàêðûòèå áàçû äàííûõ Завершает работу с базой данных метод Quit. Окно Access при этом также закрывается. Пример, закрывающий базу данных с выдачей запроса на сохранение внесенных изменений: DoCmd.Quit acQuitPrompt  см. также в гл. 9 раздел “Объектная модель Outlook 2000”.

Глава 8 VBA-ïðîãðàììèðîâàíèå: ïðèìåðû è èëëþñòðàöèè В предыдущих главах было совершено своеобразное путешествие по основным приложениям пакета MS Office, с целью изучения возможностей макросов VBA для автоматизации типичных задач. Ни сам программный пакет MS Office, ни возможности VBA-программирования в приложениях Office, конечно же, этим не исчерпываются. В состав MS Office входят и другие приложения, а также множество вспомогательных инструментальных средств. Макросы VBA с успехом работают в презентациях MS PowerPoint и в формах MS Outlook, наконец, даже в тех приложениях, которые были затронуты (MS Word, MS Excel, MS Access), осталось множество тем, о которых не было сказано ни слова.  см. подробнее о программировании в MS Outlook в гл. 9 “Программирование в Outlook: документооборот и электронная почта”.

Кроме того, в рабочей среде Office, с точки зрения VBA-программирования, существует множество объектов, которые вообще не относятся ни к одному из приложений, вернее сказать, относятся сразу ко всем приложениям. Это объекты самого пакета MS Office, которые обслуживают интегрированную среду приложений Office, те объекты, которые обеспечивают, в частности, пользовательский интерфейс. Например, знаете ли вы, что любое приложение (допустим, Word) при помощи VBA-кода можно изменить до неузнаваемости, преобразовав не только внешний вид окна приложения, но и “переработав” полностью его систему меню и панели инструментов? При помощи макросов VBA можно создать собственную версию Word (которая будет разительно отличаться от Word в стандартном варианте), приспособленную для решения специфичных для конкретного пользователя задач. В такой области, как форматирование (и вообще — изменение внешнего вида) объектов MS Office осталось множество не упомянутых вещей. Попробуем совершить небольшую “обзорную экскурсию” по миру VBA-программирования в среде MS Office, чтобы посетить те места, которые были пропущены, но где стоит побывать. Сделаем это в форме множества небольших примеров VBA-кода, снабженных иллюстрациями и пояснениями. Речь не идет о законченных процедурах-макросах. Здесь будем приведен программный код в виде нескольких строк исходного текста, и описаны результаты его выполнения. Любой из этих фрагментов может быть использован в пользовательском макросе или процедуре обработки события. Чтобы проверить подобный пример в действии и поэкспериментировать с ним, можно создать макрос с произвольным именем, например, при помощи команды Ñåðâèñ | Ìàêðîñ | Ìàêðîñû или любым другим способом. В окне редактора Visual Basic любой макрос, в тексте которого находится курсор ввода, можно запустить нажатием клавиши [F5], а при помощи клавиши [F8] можно выполнять макрос в режиме отладки, построчно.

Программирование объектов интегрированной среды MS Office 191

Ïðîãðàììèðîâàíèå îáúåêòîâ èíòåãðèðîâàííîé ñðåäû MS Office Вот примеры кода, работающего с объектами интегрированной среды MS Office. Все упоминаемые здесь объекты не относятся к какому-либо приложению. На рис. 8.1 изображен фрагмент окна MS Word, иллюстрирующий результат выполнения программного кода, однако в MS Excel этот фрагмент выполнит точно такую же операцию, только по отношении к панели инструментов Excel. Никаких ссылок задавать для этого не требуется, поскольку речь идет об объектах, общих для всех приложений Office.

Ïàíåëè èíñòðóìåíòîâ è ìåíþ â ïðèëîæåíèÿõ MS Office Îáúåêòû òèïà CommandBar В объектной модели Office все панели инструментов и меню представлены объектами типа CommandBar. Каждая панель инструментов, строка меню, каждое меню в строке меню, и даже подменю, заключающееся в пункте меню — это объект типа CommandBar. Все объекты CommandBar приложения (не имеет значения, о каком приложении идет речь — макрос VBA может даже “не знать”, в какой именно среде он работает) объединены в семейство CommandBars данного приложения. Îáúåêòû òèïà CommandBarControl Находящиеся на панели инструментов элементы управления представлены объектами типа CommandBarControl, которые объединены в семейство CommandBarControls. Вообще говоря, дело обстоит несколько сложнее, но для решения простейших задач такого представления вполне достаточно. Как знает каждый пользователь, занимавшийся настройкой среды хотя бы одного из приложений MS Office 2000, панели инструментов подвержены самым различным изменениям. В окне приложения может присутствовать множество панелей или же ни одной. Все инструментальные панели, доступные в меню Âèä | Ïàíåëè èíñòðóìåíòîâ. Кнопки и прочие элементы управления можно убрать с панели или поместить на любую панель. То же самое касается и меню. Набор меню и команд каждый пользователь настраивает произвольным образом при помощи диалогового окна Ñåðâèñ | Íàñòðîéêà. Ìåòîä FindControl: ïîèñê ýëåìåíòà óïðàâëåíèÿ По этой причине довольно затруднительно было бы оперировать жестко заданными элементами управления на жестко заданных панелях инструментов. Чтобы обратиться к конкретному элементу управления, используют метод FindControl семейства CommandBars. Этот метод способен найти элемент управления по его “личному номеру” — коду-идентификатору. Чтобы посмотреть на деле, как можно управлять элементами управления на инструментальных панелях MS Office, объявим переменную типа CommandBarControl: Dim MyCon As CommandBarControl Затем используем метод FindControl семейства CommandBars текущего приложения, чтобы присвоить переменной в качестве значения кнопку Îòêðûòü на панели инструментов Ñòàíäàðòíàÿ (идентификатор этой кнопки — число 23). Set MyCon = CommandBars.FindControl(ID:=23) Теперь переменная MyCon представляет хорошо всем знакомую кнопку (см. рис. 8.1), при помощи которой пользователи, обычно, открывают документы приложения

192 Глава 8. VBA-программирование: примеры и иллюстрации ÏÐÈÌÅ×ÀÍÈÅ

Еще раз укажим на то, что на рис. 8.1 изображен фрагмент окна Word, однако все сказанное в равной мере относится к любому приложению Office, где есть панель инструментов Стандартная.

Рис. 8.1. Кнопка Открыть присутствует на панели инструментов Стандартная большинства приложений Office

Рис. 8.2. Кнопка Открыть получила рисунок, идентичный рисунку кнопки Вырезать, однако функции кнопки от этого не поменялись

Ñâîéñòâî FaceId: èçìåíåíèå çíà÷êà íà ýëåìåíòå óïðàâëåíèÿ Если теперь обратиться к переменной MyCon, то можно что-нибудь сделать с кнопкой Îòêðûòü. Например, чтобы заменить рисунок (значка) на кнопке, следует изменить значение свойства FaceId (идентификатор “лица” кнопки): MyCon.FaceId = 21 В результате кнопка Îòêðûòü “примет обличье” кнопки Âûðåçàòü, однако на выполняемые кнопкой функции это никак не повлияет.

Ñîçäàíèå ïîëüçîâàòåëüñêîé ïàíåëè èíñòðóìåíòîâ Немного усложним теперь постановку задачи. Будем по-прежнему использовать “готовую” кнопку Îòêðûòü, однако поместим ее на другую панель инструментов. На какую? На пользовательску (собственную) панель инструментов, которая была создана специально для этой цели! Теперь, кроме переменной MyCon, потребуется переменная типа CommandBar, назовем ее MyCBar: Dim MyCBar As CommandBar Dim MyCon As CommandBarControl Ìåòîä Add Далее, чтобы создать новую панель инструментов, необходимо добавить новый элемент в семейство CommandBars. Как всегда, эта задача решается при помощи метода Add: Set MyCBar = CommandBars.Add(Name:="Моя панель инструментов", _ Position:=msoBarTop, Temporary:=True) При помощи именованного параметра Name здесь задается имя пользовательской панели инструментов, а параметр Position определяет ее местоположение (msoBarTop означает прикрепление панели к верхней границе окна). Логический параметр Temporary, имеющий по умолчанию значение False, получает здесь значение True. Благодаря этому панель будет создана с временным статусом — после окончания данного сеанса работы с приложением она исчезнет. Ñâîéñòâî Visible Далее остается сделать панель инструментов видимой присваиванием значения True ее свойству Visible: MyCBar.Visible = True

Программирование объектов интегрированной среды MS Office 193 В этот момент панель появится на экране, однако она пока что пуста. Чтобы поместить на нее кнопку Îòêðûòü, необходимо вызвать метод Add, принадлежащий семейству Controls этой панели, то есть, в данном случае, объектной переменной MyCBar. Чтобы затем можно было обратиться к свойствам этой кнопки, одновременно следует присвоить ее в качестве значения заранее объявленной объектной переменной MyCon: Set MyCon = MyCBar.Controls.Add(Type:=msoControlButton, ID:=23) Теперь можно обращаться к кнопке на панели инструментов при помощи переменной MyCon. Например, назначим для этой кнопки рисунок, соответствующий кнопке Îòêðûòü: MyCon.FaceId = 23 Панель Ìîÿ ïàíåëü èíñòðóìåíòîâ стала теперь равноправной панелью инструментов приложения (рис. 8.3). Как и любую другую панель, ее можно вывести на экран или же отключить. Чтобы убедиться в этом, достаточно раскрыть меню Âèä | Ïàíåëè èíñòðóìåíòîâ (рис. 8.4).

Рис. 8.3. Кнопка Открыть с соответствующим рисунком на своей поверхности помещена на вновь созданную пользовательскую панель инструментов

Рис. 8.4. Созданная макросом VBA пользовательская панель инструментов ничем не отличается от остальных панелей инструментов приложения

Ñîçäàíèå íîâîé êíîïêè Впрочем, стандартные кнопки панелей Office мало, где могут пригодиться. Гораздо интересней другая задача: как снабдить существующую или новую панель совершенно новой кнопкой (или иным элементом управления), которая приводила бы в действие пользовательский макрос, то есть выполняла бы совершенно новую, созданную специально для решения какой-то задачи, пользовательскую команду. Предположим, требуется обеспечить для пользователя возможность запуска макроса с именем MySub. Для иллюстрации этого принципа используем простейшую процедуру, которая всего лишь выводит на экран сообщение: Sub MySub() MsgBox "Выполнен пользовательский макрос" End Sub

194 Глава 8. VBA-программирование: примеры и иллюстрации Для того чтобы связать процедуру MySub с кнопкой на панели инструментов, необходимо вначале такую кнопку создать. Для этого следует объявить объектную переменную типа CommandBarButton: Dim MyCBut As CommandBarButton Предположим, что эту кнопку надо поместить на панель инструментов Ñòàíäàðòíàÿ. В этом случае необходимо воспользоваться методом Add, принадлежащим семейству Controls именно этой панели (обратиться к панели Ñòàíäàðòíàÿ можно, как к элементу семейства, используя оригинальное имя панели — “Standard”): Set MyCBut = _ CommandBars("Standard").Controls.Add(Type:=msoControlButton) Параметр Type определяет тип добавляемого элемента управления, при этом значение msoControlButton соответствует обычной кнопке. После того, как кнопка создана, хотя и не появилась еще на панели Ñòàíäàðòíàÿ, можно выполнить с ней необходимые преобразования, используя свойства переменной MyCBut: With MyCBut .Style = msoButtonCaption .OnAction = "MySub" .Caption = "Мой Макрос" End With Задавая для свойства Style значение msoButtonCaption, можно определить тип кнопки — “кнопка с надписью” (возможны также варианты “кнопка с рисунком” и “кнопка с надписью и рисунком”). Строка, загруженная в свойство OnAction, представляет собой имя макроса MySub, который теперь подлежит выполнению по щелчку на новой кнопке. Чтобы назначение кнопки было понятным без объяснений, снабдим ее надписью “Мой Макрос” (свойство Caption). Наконец, остается сделать кнопку видимой: MyCBut.Visible = True

Рис. 8.5. На панели инструментов Стандартная появилась новая кнопка, служащая для запуска пользовательского макроса

Рис. 8.6. Новая кнопка на панели инструментов Стандартная сработала, запустив пользовательский макрос

В результате кнопка Ìîé Ìàêðîñ, готовая к действию, появится на панели инструментов Ñòàíäàðòíàÿ (рис. 8.5).

Чтобы убедиться в работоспособности кнопки, достаточно щелкнуть на ней мышью и на экране появится окно-сообщение, показанное на рис. 8.6, и являющееся результатом выполнения макроса MySub.

Ñîçäàíèå ïîëüçîâàòåëüñêîãî ìåíþ Как уже отмечалось, меню и подменю интегрированной среды Office представляют собой такие же, в сущности, объекты, что и панели инструментов. Давайте посмотрим, каким образом можно создать целое меню и добавить его в строку главного меню приложения.

Программирование объектов интегрированной среды MS Office 195 Предположим, уже подготовлен не один, а целых два макроса, к которым необходимо предоставить простой и понятный доступ пользователей. Пусть эти два условных макроса называются Команда1 и Команда2: Sub Команда1() MsgBox "Выполнена команда 1" End Sub Sub Команда2() MsgBox "Выполнена команда 2" End Sub Чтобы получить доступ к строке меню текущего приложения, потребуется переменная типа CommandBar, а для выполнения действий с этим меню — две переменные типа CommandBarControl. Одна из них будет представлять новое меню, а вторая — команды в нем: Dim MyCBar As CommandBar Dim MyCon As CommandBarControl Dim MyMenu As CommandBarControl Чтобы переменная MyCBar представляла строку меню текущего приложения, достаточно выполнить присваивание: Set MyCBar = CommandBars.ActiveMenuBar Теперь все меню содержатся в семействе Controls переменной MyCBar, и, чтобы добавить туда еще одно меню, следует вызвать метод Add: Set MyMenu = _ MyCBar.Controls.Add(Type:=msoControlPopup, Temporary:=True) Параметр Type задает тип элемента управления — Ðàñêðûâàþùååñÿ ìåíþ, а при помощи параметра Temporary меню становится временным — после закрытия окна приложения оно исчезнет из строки меню. При Temporary = True меню останется в строке также и при последующих сеансах работы с приложением. Далее создадим надпись на меню: MyMenu.Caption = "Мое меню" Пока что меню пусто, в нем не содержится ни одной команды. Чтобы исправить положение, воспользуемся второй переменной CommandBarControl и методом Add семейства элементов управления меню, то есть семейства Controls переменной MyMenu. Может показаться странным, что само меню в целом, и каждая команда в нем в отдельности, представлены переменными одного и того же типа. Но на самом деле это вполне логично — каждая команда в меню может содержать в себе подменю — точно такое же меню, каждая команда в котором может... и так далее. Set MyCon = _ MyMenu.CommandBar.Controls.Add(Type:=msoControlButton, ID:=1) Параметр ID задает идентификатор команды — ее порядковый номер в меню, по которому к ней можно будет впоследствии обращаться. В результате выполнения этого оператора переменная MyCon становится “временным представителем” новой команды, добавленной в новое меню Ìîå ìåíþ, которое, в свою очередь, представлено переменной MyMenu. Воспользуемся этим моментом и настроим команду меню соответствующим образом:

196 Глава 8. VBA-программирование: примеры и иллюстрации With MyCon .Caption = "Мое меню 1" .TooltipText = "Мое меню 1" .Style = msoButtonCaption .OnAction = "Команда1" End With При помощи свойства Caption задается надпись элемента меню. Свойство Style определяет тип этого элемента управления. А значение свойства OnAction назначает команде макрос, который будет выполняться при выборе данного пункта меню. Чтобы создать вторую команду в новом меню, снова воспользуемся переменной MyCon, которая теперь потеряет связь с созданным и настроенным уже первым пунктом, и станет представителем второго пункта меню: Set MyCon = _ MyMenu.CommandBar.Controls.Add(Type:=msoControlButton, ID:=2) Настроим вторую команду меню аналогичным образом, изменим лишь надпись и текст подсказки (свойство TooltipText), а для свойства OnAction укажем имя другого макроса: With MyCon .Caption = "Мое меню 2" .TooltipText = "Мое меню 2" .Style = msoButtonCaption .OnAction = "Команда2" End With В результате выполнения описанного выше кода в строке главного меню приложения появится новое меню, в котором содержатся две команды (рис. 8.7).

Рис. 8.7. В строке главного меню приложения появилось пользовательское меню, содержащее в себе две команды

Рис. 8.8. Выполнена одна из команд пользовательского меню

Выбрав одну из этих команд, легко удостовериться в том, что выполняется при этом соответствующий команде макрос (см. рис. 8.8).

Äîáàâëåíèå ïîëüçîâàòåëüñêîé êîìàíäû â ñòàíäàðòíîå ìåíþ ïðèëîæåíèÿ MS Office Сходным образом можно встроить собственную пользовательскую команду в одно из существующих меню приложения. Чтобы получить доступ к одному из меню в строке главного меню, необходимо обратиться все к тому же семейству Controls, однако, не добавляя новый элемент при помощи метода Add, а просто указывая порядковый номер одного из существующих элементов. Предположим, требуется встроить свою команду или подменю в меню Ôàéë. Для этого надо, во-первых, получить в свою объектную переменную ссылку на строку меню текущего приложения,… Set MyCBar = CommandBars.ActiveMenuBar

Программирование объектов интегрированной среды MS Office 197 а во-вторых, в переменную типа CommandBarControl загрузить требуемый элемент строки: меню Ôàéë первое по счету, и поэтому к нему можно обратиться, как к элементу с номером 1: Set MyMenu = MyCBar.Controls(1) Теперь переменная MyMenu указывает на меню Ôàéë, и все команды этого меню доступны посредством ее семейства Controls. Чтобы добавить туда еще одну команду, следует воспользоваться методом Add этого семейства и еще одной объектной переменной типа CommandBarControl (предполагается, что все упомянутые переменные уже объявлены, как это было сделано в предыдущем примере): Set MyCon = _ MyMenu.CommandBar.Controls.Add(Type:=msoControlButton, ID:=1) Далее остается настроить свойства новой команды соответствующим образом. Пусть команда называется Ìîÿ êîìàíäà и пусть при выборе ее выполняется макрос с именем МойМакрос1:

Рис. 8.9. Пользовательская команда Ìîÿ êîìàíäà, запускающая макрос МойМакрос1, добавлена в меню Файл With MyCon .Caption = "Моя команда" .TooltipText = "Моя команда" .Style = msoButtonCaption .OnAction = "МойМакрос1" End With На рис. 8.9 показано меню Ôàéë с новой командой.

Ïðîãðàììèðîâàíèå ïîìîùíèêà Office Каждый, кто пользовался пакетом MS Office, так или иначе сталкивался с помощником — забавным мультипликационным персонажем, который дает советы, помогает найти нужную справку, а иногда просто развлекает пользователя. Ñêðåïûø и Êîëîáîê, Ô1 и Ìóðêà — существует целый ряд “актеров” в роли помощника, при этом каждый обладает своими забавными особенностями. Как и любой объект MS Office, поведене помощника можно запрограммировать при помощи

198 Глава 8. VBA-программирование: примеры и иллюстрации VBA. Если, при разработке какого-нибудь макроса, необходимо обеспечить пользователю возможность получения справки или просто дать подсказку, то наилучший способ сделать это заключается в использовании объекта Assistant, то есть помощника Office.

Îáúåêò Assistant Каким образом должен запускаться программный код, обращающийся к объекту Assistant? Существует несколько возможностей. Например, такой макрос можно привязать к существующей команде меню или добавить для него в меню специальную команду. Можно запускать такую процедуру при помощи кнопки на панели инструментов.

Îáðàáîòêà îøèáîê ñ èñïîëüçîâàíèåì îïåðàòîðà On Error GoTo Если в макросе предусмотрена обработка ошибок, то для ошибок пользователя можно предусмотреть специальный код, использующий помощника для выдачи сообщений или подсказок. Чтобы сделать это, следует использовать оператор On Error GoTo. Условный пример его использования покзан на листинге 8.1. ËÈÑÒÈÍÃ 8.1

Sub MySub() On Error GoTo MyError1 … …код макроса … Exit Sub MyError1: … …код обработки ошибки … End Sub Оператор On Error GoTo помещают в самое начало процедуры, то есть туда, где никаких ошибок быть еще не может. В качестве параметра указывают метку перехода по ошибке — сама метка, сопровождаемая двоеточием, должна при этом находиться в том месте кода, куда VBA передаст управления в случае возникновения какой-нибудь ошибки. Чтобы этот код не выполнялся всегда, его обычно размещают в конце процедуры, после всего полезного кода макроса, и предваряют его оператором Exit Sub, который принудительно завершает выполнение процедуры. Таким образом, при нормальном выполнении макроса код обработки ошибок никогда не получит управление. Но если возникнет ошибка, этот код сразу же “сработает”.

Ñîçäàíèå ïîëüçîâàòåëüñêîé Ñïðàâêè Наконец, в каких-то случаях, можно создать специальную командную кнопку Ñïðàâêà, по щелчку на которой будет выполняться код, обеспечивающий для пользователя поддержку помощника Office. Давайте используем этот простейший способ для того, чтобы снабдить поддержкой помощника бланк заказа на сборку персонального компьютера из главы 2. Создание кнопки Ñïðàâêà на рабочем листе Excel, полагаем, не вызовет у читателя никаких затруднений, поэтому сосредоточимся на коде VBA, который запрограммирует помощника на выдачу справок-подсказок по заполнению бланка заказа.  см. подробнее в гл. 1 раздел “Как поместить на лист элемент управления”.

Объект Assistant уникален, он не является элементом какого-либо семейства и в равной мере принадлежит всем приложениям Office. По этой причине не требуется никаких специальных переменных или ссылок для его использования — в любом макросе VBA, о каком бы приложении и объекте ни шла речь, можно вставить следующий оператор: … Assistant.Visible = True

Программирование объектов интегрированной среды MS Office 199 … и помощник Office (в том обличье, на которое он настроен пользователем), появится на экране.

Рис. 8.10. Помощник Office в исполнении Мурки Ïåðåìåííàÿ òèïà Balloon Чтобы “научить” Ìóðêó выдавать подсказки пользователю по конкретной теме “заполнение бланка заказа”, нам потребуются всего две переменные: Dim MyBal As Balloon Dim UserChoice As Integer Переменная типа Balloon представляет окна сообщений (“баллоны”) помощника, а смысл целочисленной переменной UserChoice будет раскрыт дальше. Ñâîéñòâî Visible Итак, последовательно пройдем все фазы работы помощника. Вначале, чтобы сделать помощника видимым, присвоим значение True его свойству Visible: Assistant.Visible = True Ñâîéñòâî Animation Чтобы сопроводить появление помощника на экране небольшой анимацией “приветствие”, используем оператор: Assistant.Animation = msoAnimationGreeting Каждый из помощников по-своему разыгрывает сценку приветствия — Мурка, например, выпрыгивает из небольшого окошка на краю экрана. Ìåòîä NewBalloon Далее необходимо создать окно сообщения помощника — объект типа Balloon. Не существует семейства Balloons, хотя объектов Balloon можно создать много и использовать их все одновременно. Новый объект такого типа создается при помощи метода NewBalloon помощника Office: Set MyBal = Assistant.NewBalloon Ñâîéñòâà Heading è Text Теперь необходимо настроить Balloon-объект так, как требуется. Вначале, при помощи свойств Heading и Text зададим заголовок окна сообщения и непосредственно текст сообщения: MyBal.Heading = "Инструкции по заполнению бланка заказа" MyBal.Text = "Вы хотите узнать:" Ñâîéñòâî Button Свойство Button определяет набор кнопок в окне сообщения, msoButtonSetOkCancel при этом соответствует набору ”OK и Îòìåíà”:

константа

200 Глава 8. VBA-программирование: примеры и иллюстрации MyBal.Button = msoButtonSetOkCancel Ñâîéñòâî Labels Для того чтобы пользователь мог выбрать одну из тем предлагаемой справки, используют свойство Labels: MyBal.Labels(1).Text = "Как заполнить бланк заказа?" MyBal.Labels(2).Text = "Почему выбранный процессор " + _ "или материнская плата не остаются выбранными?" MyBal.Labels(3).Text = "Как отпечатать счет?" ÏÐÈÌÅ×ÀÍÈÅ

При наборе строковых значений в окне редактора Visual Basic можно ввести сколь угодно длинную строку. Однако часто, чтобы вся строка была видна на экране полностью, строку, как уже отмечалось, разбивают при помощи пробела и знака подчеркивания. В случае строковых констант дело осложняется тем, что текстовые строки разбивать подобным образом нельзя — редактор Visual Basic такое значение “не поймет”. В подобных случаях одну строку представляют как объединение нескольких строковых констант, которые, затем, разбивают при помощи пробела и знака подчеркивания. Строковые значения в языке Visual Basic объединяются при помощи знака + или &. Ìåòîä Show В результате окно сообщения помощника будет снабжено тремя надписями, причем щелчком на любой из них пользователь закроет окно сообщения, а объект MyBal вернет номер надписи, на которой щелкнул пользователь. Чтобы не потерять это значение, при выводе на экран Balloonобъекта посредством метода Show следует сохранить возвращаемое число в целочисленной переменной: UserChoice = MyBal.Show После выполнения этого оператора над помощником появится окно сообщения, как показано на рис. 8.11.

Рис. 8.11. Помощник Office вездесущ. Если код макроса выполняется в окне редактора Visual Basic, то помощник со своим сообщением появится прямо над текстом макроса

Программирование объектов интегрированной среды MS Office 201 Ðåàêöèÿ ïîìîùíèêà íà âûáîð ïîëüçîâàòåëÿ Далее можно изобразить “бурную деятельность” помощника изменив свойство Animation, присвоением новой константы: Assistant.Animation = msoAnimationCheckingSomething Анимация сценки msoAnimationCheckingSomething выглядит по-разному у разных помощников. Ìóðêà, например, надевает очки, берет блокнот и карандаш, и начинает что-то сосредоточенно читать. В этот момент на экране настанет пора появиться следующему окну сообщения. Поскольку это должно быть одно из трех окон, соответствующее одному из трех выбранных вопросов, необходимо вначале определить — какое именно сообщение должно появиться на экране. Это нетрудно сделать, проанализировав при помощи конструкции Select Case число, содержащееся в переменной UserChoice: Select Case UserChoice Case 1 ... Case 2 ... Case 3 ... End Select  см. также в приложении раздел “Конструкция Select Case … End Select”.

Чтобы задать параметры окна сообщения, то есть Balloon-объекта, для каждого из трех случаев, можно воспользоваться той же самой переменной MyBal и методом NewBalloon объекта Assistant. Необходимо загрузить соответствующие строковые значения в свойства Heading (заголовок) и Text (собственно текст сообщения). Затем созданный и настроенный Balloonобъект необходимо отобразить на экране вызовом метода Show. Необходимо заметить, что созданные Balloon-объекты продолжают существовать, пока соответствующей объектной переменной не присвоено другое значение. Если бы было использовано несколько переменных типа Balloon, и использованы для создания следующих окон другие переменные, “не трогая” переменную MyBal, то первоначальное окно сообщения можно было бы вновь, в любой момент отобразить на экране при помощи метода Show. Но в этом нет необходимости. В данном случае используем только одну переменную MyBal.

Рис. 8.12. Помощник обработал выбор пользователя и выдал уточняющее сообщение, изображая при этом бурную деятельность в поисках справки

202 Глава 8. VBA-программирование: примеры и иллюстрации Вот, какой код должен быть выполнен, чтобы на экране появилось следующее, одно из трех окон, соответствующее выбранному пользователем вопросу: Select Case UserChoice Case 1 Set MyBal = Assistant.NewBalloon MyBal.Text = "Необходимо выбрать в " + _ "раскрывающихся списках компоненты " + _ "компьютера. Общая цена изделия при " + _ "этом отображается в нижней строке." MyBal.Heading = "Заполнение бланка заказа" MyBal.Show Case 2 Set MyBal = Assistant.NewBalloon MyBal.Text = "Некоторые материнские платы " + _ "несовместимы с некоторыми процессорами. " + _ "Такие сочетания недопустимы." MyBal.Heading = "Несовместимость компонентов" MyBal.Show Case 3 Set MyBal = Assistant.NewBalloon MyBal.Text = "Для того чтобы отпечатать счет, " + _ "следует вначале убедиться, что " + _ "выбранная в бланке конфигурация " + _ "компьютера и его цена соответствуют " + _ " вашим намерениям. " + _ "Затем следует щелкнуть на кнопке Счет." MyBal.Heading = "Печать счета" MyBal.Show End Select В результате над Ìóðêîé, усердно ищущей справку в блокноте, появится выбранная справка, как изображено на рис. 8.12. Последней строкой кода рассматриваемого примера будет строка, убирающая помощника с экрана (до ее выполнения очередь не дойдет до тех пор, пока пользователь не щелкнет на кнопке OK в окне сообщения помощника): Assistant.Visible = False

Ôîðìàòèðîâàíèå îáúåêòîâ ïðèëîæåíèé MS Office Форматирование документов, и вообще — объектов приложений MS Office, представляет собой весьма обширную область VBA-программирования. В основном, форматирование сводится просто к настройке определенных свойств тех или иных объектов, но число этих свойств и объектов огромно. Однако нет причин отчаиваться — при всем своем многообразии, объекты MS Office образуют собой весьма стройную и логичную систему, причем, это особенно верно в той части, что касается форматирования. Например, любой объект, обладающий свойством Font, позволяет настроить свойства шрифта, который в этом объекте используется. Идет ли речь о символе, слове или абзаце в документе Word, текстовом значении в ячейке Excel или надписи на форме Access — в любом случае свойство объект.Font.Size позволит задать размер шрифта, а присваивание объект.Font.Bold = True сделает шрифт полужирным.

Форматирование объектов приложений MS Office 203 Другой пример — свойство Borders. Обычно, в нем содержится семейство границ объекта, его Border-объектов. Зная, как настраивать свойства границ для документа Word, можно без большого напряжения “разобраться” с границами абзаца или диапазона ячеек на рабочем листе Excel. Словом, при всей сложности и при всем многообразии форматирующих свойств объектов в приложениях MS Office, в них выдержаны четкие, единые принципы и подходы, позволяющие пользователю примерно одними и теми же способами работать с различными видимыми объектами в разных приложениях Office. Чтобы поближе познакомиться с этими принципами, давайте совершим небольшую экскурсию по некоему документу Word, применив к нему различные способы форматирования, и посмотрим, как это выглядит на практике.

Ïðèìåð ôîðìàòèðîâàíèÿ äîêóìåíòà Word Прежде всего, позаботимся о тексте. Поскольку речь идет о совершенно абстрактном документе, который требуется лишь для упражнений в форматировании, его содержимое не имеет никакого значения. Единственное требование, предъявляемое к этому документу состоит в том, чтобы он включал в себя различные структурные элементы — заголовок, обычный текст, список, подпись и т.д.

Ôîðìàòèðîâàíèå àáçàöà Сформируем его, например, следующим образом. Объявив предварительно строковую переменную MyText, Dim MyText As String наполним ее строкой, которая содержит в себе весь текст документа, разбитый на абзацы: MyText = "Коммерческое предложение" + vbCr + _ "ООО Универсал" + vbCr + _ "ООО Универсал предлагает к реализации" + _ " неограниченные партии замечательного товара. " + _ "Товар предлагается в ассортименте из " + _ "трех наименований: " + vbCr + _ "Наименование товара 1" + vbCr + _ "Наименование товара 2" + vbCr + _ "Наименование товара 3" + vbCr + _ "С уважением, - И.И.Иванов" + vbCr Èñïîëüçîâàíèå êîíñòàíòû vbCr Конструкцию вида "Строка… " + vbCr + _ "Строка… " используют в случаях, когда необходимо получить строковое значение, состоящее из нескольких строк (константа vbCr — это символ конца строки). В случае документа Word символ vbCr рассматривается, как символ абзаца; поэтому, если сделать теперь переменную MyText содержимым документа Word, то такой документ будет состоять из нескольких абзацев. Первые два абзаца соответствуют заголовку сообщения и наименованию организации. Третий абзац в коде VBA формируется из нескольких строк, но, поскольку символ vbCr к ним не добавляется, все они вместе представляют один абзац — собственно текст сообщения. Далее следуют три абзаца, представляющие собой список из трех наименований. Наконец, завершает текст абзац-подпись.

204 Глава 8. VBA-программирование: примеры и иллюстрации Èñïîëüçîâàíèå ìåòîäà InsertBefore Чтобы содержащаяся в переменной MyText столь сложная строка стала содержимым документа Word (предполагается, что обсуждаемый код размещен в окне редактора Visual Basic, которое открыто из окна пустого документа Word), необходимо вызвать метод InsertBefore (“вставка вначале”): ActiveDocument.Content.InsertBefore MyText Ñâîéñòâî Content Свойство Content активного документа представляет его текстовое содержимое. В результате такого присваивания сформированный в переменной MyText текст будет вставлен в документ Word, как семь абзацев (рис. 8.13). Пусть это и будет та тот документ, который и будет подвержен форматированию при помощи VBA-кода. Как обращаться к абзацам документа, уже было описано в предыдущей главе.  см. в гл. 7 раздел “Семейства Paragraphs, Sentences, Words и Characters”.

Абзац, то есть объект типа Paragraph, обладает более чем сорока свойствами, и большинство из них имеют непосредственное отношение к форматированию. Кроме того, во многих свойствах абзаца содержатся объекты, которые сами обладают десятками свойств. Можно менять не только шрифт и все, связанные с ним параметры, но и границы, отступы, интервалы, выравнивание и переносы… это мог бы быть очень длинный список. Ñâîéñòâî Style К счастью, есть более простой способ справиться с форматированием абзаца. Как и многие другие объекты в среде Office, абзац обладает свойством Style, которое задает один из предопределенных стилей. Присвоив абзацу один из стандартных стилей, можно, тем самым, определите значение сразу множества его свойств.

Рис. 8.13. В пустой документ Word вставлен простой текст, никак не отформатированный В локализованной русской версии Office стиль можно присвоить двумя способами: использовать стандартную константу, соответствующую стилю, или же указать просто русское имя стиля. Заголовок сообщения Например, присвоить абзацу стиль Çàãîëîâîê ñîîáùåíèÿ можно при помощи оператора:

Форматирование объектов приложений MS Office 205 Абзац.Style = “Шапка” Или использовать стандартный способ: Абзац.Style = wdStyleMessageHeader Будем придерживаться стандартного способа, поскольку он более универсален. Итак, сделаем первый абзац заголовком сообщения: ActiveDocument.Paragraphs(1).Style = wdStyleMessageHeader Второй абзац содержит наименование организации: ActiveDocument.Paragraphs(2).Style = wdStyleCaption Простой текст Далее следует большой абзац, в котором содержится собственно текст сообщения. Ему присвоим стиль “простой текст”: ActiveDocument.Paragraphs(3).Style = wdStylePlainText Списки Три абзаца подряд представляют собой список. Существует множество разновидностей списков. Пусть, например, это будет маркированный список 2-го вида: ActiveDocument.Paragraphs(4).Style = wdStyleListBullet2 ActiveDocument.Paragraphs(5).Style = wdStyleListBullet2 ActiveDocument.Paragraphs(6).Style = wdStyleListBullet2 Подпись Наконец, последний абзац представляет собой подпись автора под документом. На этот случай имеется стиль, который так и называется — “подпись”: ActiveDocument.Paragraphs(7).Style = wdStyleSignature Èçìåíåíèå ðàçìåðà øðèôòà Чтобы сделать текст легко читаемым и лучше рассмотреть форматирование абзацев, увеличим шрифт во всем документе, обратившись к свойству Font, принадлежащему Range-объекту всего документа: ActiveDocument.Range.Font.Size = 22 В результате всех этих действий документ преобразится примерно так, как показано на рис. 8.14.

206 Глава 8. VBA-программирование: примеры и иллюстрации

Рис. 8.14. К семи абзацам текста применены стандартные стили Каждый из этих абзацев можно было бы отформатировать точно так же (или сотней других способов), изменяя многочисленные свойства абзаца, но лишь в редких случаях можно сэкономить массу времени, воспользовавшись стандартными стилями. Всего лишь несколько присваиваний, и документ приобрел вполне пристойный вид. Îáùèé âèä äîêóìåíòà: ïðèìåð èçìåíåíèÿ ãðàíèö äîêóìåíòà (ðàçäåëà) Оставим пока в покое абзацы, и обратимся к общему виду страницы документа. Например, чтобы снабдить страницу границами, потребуется переменная типа Border, которую, конечно, необходимо объявить заранее: Dim MyBorder As Border У документа, как такового, нет семейства границ. Дело в том, что подобно колонтитулам, нумерации страниц и многому другому, границы являются принадлежностью раздела документа, а не самого документа (наверное, потому, что в каждом разделе все эти свойства могут быть определены отдельно). Поэтому, чтобы добраться до элементов форматирования страниц документа, потребуется обратиться к его первому (и, в данном случае, единственному) разделу. Общий принцип обращения к границам состоит в том, что элементы семейства Borders определяются индексом-константой. Не имеет значения, о границах чего идет речь — документа или абзаца, символа или текстового диапазона, столбца, ячейки или строки таблицы — в любом случае такое выражение, как, например, объект.Borders(wdBorderBottom) вернет нижнюю границу, а объект.Borders(wdBorderLeft) — левую.

Форматирование объектов приложений MS Office 207

Рис. 8.15. Страница документа снабжена границами Пусть требуется изменить сразу все границы. Лучше всего, в этом случае, воспользоваться конструкцией For Each … Next, которая перебирает все границы (объекты семейства Border), без необходимости указывать индекс каждого объекта в отдельности. Вот код, который сразу изменяет все границы страниц первого раздела документа: For Each MyBorder In ActiveDocument.Sections(1).Borders MyBorder.ArtStyle = wdArtWoodwork MyBorder.ArtWidth = 17 Next MyBorder Свойство ArtStyle: художественный стиль границы Свойство ArtStyle определяет “художественный стиль” границы (здесь использован “деревянный” стиль, всего же их существует несколько десятков). Свойством ArtWidth: ширина границы Свойством ArtWidth задается ширина границы в пунктах. В результате документ будут снабжен художественной окантовкой, как изображено на рис. 8.15. Ãðàíèöû àáçàöà Продолжая разговор о границах, вернемся к абзацу. Предположим, нужно изменить вид первого абзаца, который отформатирован стандартным стилем “заголовок сообщения”. Изменим его нижнюю границу так, чтобы она как бы подчеркивала текст. Для этого обратимся к тому Borderобъекту в семействе границ первого абзаца, который отвечает за нижнюю границу. Выше уже описывалось, как это сделать. Для нижней границы в качестве индекса следует использовать константу wdBorderBottom. Что именно предполагается изменить в свойствах нижней границы? Пусть линия этой границы станет тройной, вида “тонкая-толстая-тонкая с малым отступом”. Для этого необходимо загрузить соответствующую константу в свойство LineStyle Borderобъекта. Вот, какое в результате получится выражение: ActiveDocument.Paragraphs(1).Borders(wdBorderBottom).LineStyle = _

208 Глава 8. VBA-программирование: примеры и иллюстрации wdLineStyleThinThickThinSmallGap

Рис. 8.16. Изменено форматирование первого абзаца — нижняя граница абзаца поменяла стиль и толщину линии, а интервал после абзаца увеличен Ñâîéñòâî SpaceAfter: îòñòóï ñíèçó Раз уж изменяется первый абзац, то освободим под ним место (оно потребуется позже), увеличив интервал после абзаца (свойство SpaceAfter): ActiveDocument.Paragraphs(1).SpaceAfter = 31 Ñâîéñòâî Alignment: ïðèìåð âûðàâíèâàíèÿ àáçàöà ñëåâà И зададим для текста в абзаце выравнивание по центру (свойство Alignment): ActiveDocument.Paragraphs(1).Alignment = _ wdAlignParagraphCenter Вид первого абзаца в результате, как показано на рис. 8.16, соответствующим образом изменился.

Ôîðìàòèðîâàíèå ãðàôè÷åñêèõ îáúåêòîâ (ñåìåéñòâî Shapes) Пожалуй, самое время вспомнить, что кроме текстовых элементов, в объектах приложений Office встречается и графика различного рода. Добавим на страницу, в качестве украшения, автофигуру “вертикальный свиток”. Все автофигуры документа доступны через семейство Shapes. Ìåòîä AddShape Добавление автофигуры в семейство Shapes мало, чем отличается от добавления объектов в любое другое семейство — при этом используется метод AddShape — единственная особенность заключается в необходимости указать координаты и размеры новой автофигуры, а также ее тип: ActiveDocument.Shapes.AddShape _ msoShapeVerticalScroll, 410, 110, 100, 30

Форматирование объектов приложений MS Office 209

Рис. 8.17. В интервал после первого абзаца помещена автофигура “вертикальный свиток” с надписью Константа msoShapeVerticalScroll задает тип “вертикальный свиток”, а параметры, следующие за ним, — координаты и размеры автофигуры. Íàäïèñü íà àâòîôèãóðå Чтобы снабдить фигуру TextFrame.TextRange:

надписью,

следует

задать

значение

ее

свойству

ActiveDocument.Shapes(1).TextFrame.TextRange = "Универсал" В результате, как изображено на рис. 8.17, на странице появится своеобразная “эмблема фирмы”.

Ôîðìàòèðîâàíèå äîêóìåíòà ïîñðåäñòâîì òåì Для режима просмотра документа Web-äîêóìåíò существует еще одна возможность, благодаря которой можно “одним махом” отформатировать документ, не вдаваясь ни в какие тонкости. Речь идет о темах, которые доступны по команде Ôîðìàò | Òåìà, и которыми можно воспользоваться также из VBA-кода. Фактически, установленные на компьютере темы представляют собой подкаталоги в каталоге Program Files\Common Files\Microsoft Shared\Themes. Название (английское) каждого такого подкаталога — это и есть имя соответствующей темы. Это имя не соответствует русскому наименованию, которое доступно в диалоговом окне Òåìà.

210 Глава 8. VBA-программирование: примеры и иллюстрации

Рис. 8.18. К документу применена тема “Ива“ Ìåòîä ApplyTheme Чтобы применить к документу какую-нибудь тему, и одним действием отформатировать документ, необходимо вызвать метод ApplyTheme. В качестве параметра следует указать строку, состоящую из имени темы (английского названия соответствующего подкаталога) и символов “111”. Например, для того чтобы применить тему “Ива” (оригинальное имя — Willow), необходим следующий оператор: ActiveDocument.ApplyTheme "willow 111" В результате окно Word автоматически перейдет в режим просмотра Web-äîêóìåíò, а все свойства выбранной темы будут применены к текущему документу (рис. 8.18).

Ïðåäñòàâëåíèå äàííûõ â MS Office Вопросы форматирования видимых объектов — не более чем часть более глобальной темы, которую можно было бы назвать “представлением данных”. Одни и те же данные, например, таблицу числовых значений, можно представить множеством различных способов. “Сухие цифры” могут предстать перед пользователем в разном виде: как таблица, оформленная одним из десятков стилей; как диаграмма, построенная одним из десятков способов. И все это можно с легкостью реализовать в коде VBA для одного и того же набора числовых значений. Для того чтобы проиллюстрировать различные подходы к проблеме отображения данных, используем небольшой условный пример, данные из которого попытаемся представить различными способами.

Ïîäãîòîâêà òàáëè÷íûõ äàííûõ Предположим, что речь идет о прямоугольной таблице, в которой собраны сведения об объемах продаж по нескольким сотрудникам за несколько месяцев. Например, выбрав строку, “Иванов”, можно увидеть, каков был уровень продаж по данному сотруднику в каждом месяце. А в столбце, например, “март”, отображены мартовские объемы продаж по всем сотрудникам. Такую

Представление данных в MS Office 211 табличку проще всего реализовать на рабочем листе Excel. Например, она может выглядеть подобно изображенной таблице на рис. 8.19.

Рис. 8.19. Простая прямоугольная таблица содержит сведения о продажах по сотрудникам за несколько месяцев текущего года Не имеет значения, каким способом сформирована таблица — уже известно, что существует масса возможностей и помимо простого ввода цифровых данных вручную. В данном случае, интересно другое: каким образом можно отобразить эти данные, и как сделать это средствами VBA?

Óñëîâíûå ôîðìàòû Рассмотрим вначале возможности, связанные с самой таблицей, как она есть. Существует два направления, в которых можно было бы заняться ее усовершенствованием.

Ïîñòàíîâêà çàäà÷è Если данные в таблице представляют собой просто цифры, введенные вручную, то все, что можно с ними сделать — это “украсить” таблицу, отформатировав ее, используя границы, заливки, шрифты и т.д. Если же процесс формирования таблицы автоматизирован, то можно сделать эту таблицу “умнее”. Пусть она не просто автоформатируется, но форматируется различными способами, в зависимости от данных, которые в нее попадают. В рабочих книгах Excel для этого предусмотрены функции условного форматирования, которыми можно воспользоваться как при работе “вручную”, так и при помощи VBA-кода. Применим к подготовленной таблице несколько видов условного форматирования, которые позволят улучшить ее визуальное восприятие, и при этом останутся действовать для этой таблицы навсегда. Таким образом, когда цифровые данные в таблице будут изменятся, внешний вид каждой ячейки будет отражать произошедшие в ней изменения.

Îïðåäåëåíèå äèàïàçîíà óñëîâíîãî ôîðìàòèðîâàíèÿ Диапазон, на который предполагается наложить условные форматы, можно описать выражением ActiveSheet.Range("B2:H4"). Пусть это диапазон включает в себя только ячейки с цифровыми данными, оставляя за своими границами заголовки строк и столбцов таблицы.

Îáúåêò FormatCondition Условные форматы каждой ячейки или диапазона содержатся в объектах FormatCondition, которые объединены в семейство FormatConditions данного диапазона. Для любого диапазона, то есть Range-объекта, условные форматы можно задавать, добавляя новые элементы в его семейство FormatConditions при помощи метода Add. Пусть создаваемое условное форматирование различает три случая: “значение в ячейке меньше или равно $500”, “значение в ячейке от $500 до $1000” и “значение в ячейке равно или больше $1000”. ÏÐÈÌÅ×ÀÍÈÅ

Здесь существует ограничение: для одной и той же ячейки нельзя определить более трех условных форматов. По этой причине приведенный ниже код, где для таблицы задается как раз три условных формата, можно выполнить только один раз — попытка повторного выполнения кода для того же самого диапазона на том же самом листе приведет к ошибке. Впрочем, условные форматы

212 Глава 8. VBA-программирование: примеры и иллюстрации можно удалять при помощи метода Delete семейства FormatConditions или модифицировать при помощи метода Modify, принадлежащего конкретному FormatCondition-объекту. Ìåòîä Add: äîáàâëåíèå óñëîâíîãî ôîðìàòà Метод Add семейства FormatConditions при обращении к нему требует указать ряд параметров. Первый параметр определяет, к чему должен относиться формат — к выражению в ячейке или к содержащемуся в ней значению. В данном примере речь должна идти о значении, определяемом константой xlCellValue. Второй параметр задает оператор условия, то есть математическую операцию, которая выражает условие. Далее следует формула, выражение или значение, которое играет роль второй “половинки” условия (в роли первой “половинки”, понятно, выступает само значение ячейки). В случаях, когда требуется еще одно значение, добавляется четвертый параметр. Например, если требуется условие “значение ячейки равно 10”, то следует использовать параметры xlEqual и 10. Итак, чтобы наложить условный формат “значение в ячейке меньше или равно $500”, необходимо использовать параметры xlCellValue, xlLessEqual, 500. Вот как, в данном случае, выглядит добавление условного формата: With ActiveSheet.Range("B2:H4").FormatConditions. _ Add(xlCellValue, xlGreaterEqual, 1000) ... задаются параметры форматирования для данного условия End With

Ôîðìàòèðîâàíèå äàííûõ óäîâëåòâîðÿþùèõ óñëîâèþ Каким образом можно будет изображать суммы, не превышающие $500? В данном случае можно полностью дать волю фантазии. Пусть, например, такие суммы будут заштрихованы 25%ой штриховкой, и для их шрифта будут отменены признаки начертания курсив и полужирный. В результате получится следующая конструкция: With ActiveSheet.Range("B2:H4").FormatConditions _ .Add(xlCellValue, xlLessEqual, 500) With .Interior .Pattern = xlPatternGray25 End With With .Font .Bold = False .Italic = False End With End With

Äîáàâëåíèå âòîðîãî óñëîâíîãî ôîðìàòà Один условный формат наложен. К методу Add диапазона B2:H4 можно обратиться еще два раза, определив еще два условных формата для этих ячеек. Пусть для ячеек, значение которых больше или равно 1000, используется желтая сплошная заливка и полужирный шрифт черного цвета. Условие “больше или равно” задается константой xlGreaterEqual, а для определения

Представление данных в MS Office 213 заливки используют свойства объекта Interior: свойство ColorIndex задает цвет (желтому соответствует индекс 6), а свойство Pattern — узор заливки. Сплошная заливка задается константой xlSolid. Наконец, для обращению к параметрам шрифта используются свойства объекта Font: изменяя логическое значение свойства Bold можно устанавливать (сбрасывать) признак “полужирный”. Свойство ColorIndex задает цвет символов (черному соответствует индекс 1). Итак, в результате получим следующий фрагмент кода: With ActiveSheet.Range("B2:H4").FormatConditions. _ Add(xlCellValue, xlGreaterEqual, 1000) With .Interior .ColorIndex = 6 .Pattern = xlSolid End With With .Font .Bold = True .ColorIndex = 1 End With End With Добавление третьего условного формата Остается задать третий условный формат, соответствующий значениям между 500 и 1000. Для метода Add в этом случае потребуется дополнительный параметр, а в качестве константыоператора следует использовать xlBetween (между). Какое форматирование должно применяться к ячейке в этом случае? Например, пусть шрифт примет начертание полужирный курсив и окрасится в синий цвет: With ActiveSheet.Range("B2:H4").FormatConditions _ .Add(xlCellValue, xlBetween, 500, 1000) With .Font .Italic = True .Bold = True .ColorIndex = 5 End With End With В результате однократного выполнения всех трех фрагментов кода таблица будет менять свое форматирование в зависимости от значений, которые содержатся в ее ячейках, как показано на рис. 8.20.

Рис. 8.20. К ячейкам таблицы применено условное форматирование трех видов ← см. также в гл. 1 раздел “Условное форматирование ячеек”.

214 Глава 8. VBA-программирование: примеры и иллюстрации

Èçìåíåíèå ãðàíèö òàáëèöû Чтобы выделить таблицу в целом, отформатируем границы всего диапазона таблицы (диапазон A1:H4 включает в себя также заголовки строк и столбцов). Снабдим этот диапазон границами из “толстых двойных линий”: ActiveSheet.Range("A1:H4").Borders.LineStyle = xlDouble ActiveSheet.Range("A1:H4").Borders.Weight = xlThick В результате таблица примет вид, как на рис. 8.21.

Рис. 8.21. Таблица с условными форматами снабжена границами

Äèàãðàììû Табличное представление данных, все же, не самый наглядный способ отобразить множество числовых значений. Гораздо лучше это получается у графических объектов — диаграмм различного вида. Диаграммы MS Excel — не единственная доступная в среде Office разновидность диаграмм, но, наверное, наиболее часто используемая. В рабочей книге Excel могут существовать листы диаграммы, но на обычный рабочий лист Excel также можно поместить внедренную диаграмму.

Îáúåêò ChartObject: âñòàâêà äèàãðàììû íà ëèñò Excel Внедренная диаграмма, с точки зрения VBA, представлена объектом типа Chart, причем содержится она в своеобразном контейнере, роль которого играет объект типа ChartObject. Итак, добавим на рабочий лист с таблицей, подготовленной в предыдущем разделе, диаграмму, которая графически отображала бы содержащиеся в таблице данные. Для создания на листе контейнера для диаграммы и для создания собственно диаграммы, потребуются две переменные: Dim MyChartObject As ChartObject Dim MyChart As Chart Чтобы создать на листе контейнер для диаграммы в заданной позиции и с заданными размерами, необходимо, при обращении к методу Add семейства ChartObjects рабочего листа, указать координаты верхнего левого угла диаграммы и ее размеры. Пусть диаграмма располагается чуть ниже таблицы и пусть она будет немного шире и в несколько раз выше таблицы: Set MyChartObject = ActiveSheet.ChartObjects.Add(5, 90, 430, 210)

Представление данных в MS Office 215

Рис. 8.22. Под исходной таблицей с данными, на том же рабочем листе, создана область диаграммы, которая будет отображать содержащиеся в таблице данные в виде графика (диаграммы) Выполнив затем присваивание,… Set MyChart = MyChartObject.Chart …тем самым, в переменную MyChart будет помещена пустая диаграмма, которую можно настроить любым требующимся образом. Но, прежде чем перейти непосредственно к диаграмме, отформатируем область диаграммы, в которой она содержится, то есть объект MyChartObject. Пусть рамка контейнера обладает скругленными углами (свойство RoundedCorners) и отбрасывает тень (свойство Shadow): With MyChartObject .Name = "MyDia" .RoundedCorners = True .Shadow = True End With В результате выполнения описанного выше программного кода под таблицей появится пока пустой контейнер диаграммы с заданными характеристиками (рис. 8.22). Ìåòîä SetSourceData: ñîçäàíèå îáû÷íîé ãèñòîãðàììû Для того чтобы в созданном контейнере появилась диаграмма, отображающая нужные данные, достаточно лишь вызвать метод SetSourceData созданной диаграммы, и в качестве параметра указать диапазон A1:H4, а также, при помощи константы xlRows, указать, что развертывание оси значений следует производить по строкам таблицы (константа xlColumns задала бы считывание данных по столбцам). MyChart.SetSourceData Range("A1:H4"), xlRows MyChart.HasLegend = True В результате диаграмма приобретет свой простейший вид, “обычная гистограмма” — один из множества типов, который, тем не менее, вполне наглядно представляет содержимое таблицы (рис. 8.23).

216 Глава 8. VBA-программирование: примеры и иллюстрации

Рис. 8.23. После задания области отображаемых значений диаграмма принимает свой простейший вид — “обычная гистограмма“ Ñâîéñòâî HasLegend При помощи свойства HasLegend можно разрешить или запретить отображение легенды диаграммы. Обратите внимание на тот факт, что легенда правильно отображает имена для рядов данных “Иванов”, “Петров”, “Сидоров”, а ось значений снабжена подписями — названиями месяцев. Уверяем, программисту пришлось бы немало потрудиться, чтобы снабдить диаграмму такими атрибутами, если бы в качестве диапазона была указана область B2:H4, содержащая лишь “голые” цифровые данные. Но в данном случае был указан диапазон A1:H4, включающий строку и столбец заголовков. Метод SetSourceData самостоятельно “разобрался”, что тут к чему, и правильно все распознал. Ñâîéñòâî ChartType: çàäàíèå òèïà äèàãðàììû Тип диаграммы задается значением свойства ChartType. Объект типа Chart различает несколько десятков видов диаграмм. Например, чтобы преобразовать созданную обячную гистограмму в трехмерную гистограмму, необходимо загрузить ее в свойство ChartType константу xl3DColumn. MyChart.ChartType = xl3DColumn В результате диаграмма станет объемной, как показано на рис. 8.24.

Представление данных в MS Office 217

Рис. 8.24. Диаграмма преобразовалась в трехмерную гистограмму Ìåòîä ApplyCustomType: ñîçäàíèå ïîëüçîâàòåëüñêîãî òèïà äèàãðàìì Кроме изменения значения свойства ChartType, тип диаграммы можно изменить также при помощи метода ApplyCustomType, которому в качестве параметра можно передать те же самые константы типов, которые используются в свойстве ChartType. Впрочем, возможности метода значительно шире — он позволяет, кроме того, определять пользовательские типы диаграмм. MyChart.ApplyCustomType xl3DArea В результате вызова метода ApplyCustomType с параметром xl3DArea диаграмма преобразится в “объемную диаграмму с областями” (рис. 8.25).

Рис. 8.25. Диаграмма преобразовалась в “объемную диаграмму с областями”

218 Глава 8. VBA-программирование: примеры и иллюстрации ÏÐÈÌÅ×ÀÍÈÅ

При изменении типа диаграммы необходимо учитывать одно обстоятельство. От выбранного типа диаграммы зависит набор доступных свойств и методов Chart-объекта. Например, для двумерных диаграмм не имеют смысла многие свойства диаграмм трехмерных. Поэтому перемена типа “на лету” может приводить к ошибкам, если вопрос “совместимости” меняющихся типов недостаточно продуман. Ñâîéñòâî HasTitle: ñîçäàíèå çàãîëîâêà äèàãðàììû Чтобы снабдить диаграмму заголовком, воспользуемся свойством HasTitle. Для этого ему надо присвоить значение True: MyChart.HasTitle = True Свойство ChartTitle Без этого присваивания последующий код может привести к ошибке. Теперь, поскольку диаграмме разрешено иметь заголовок, можно обратиться к свойствам объекта ChartTitle, который представляет собой заголовок диаграммы. Зададим текст и размер шрифта для заголовка: With MyChart.ChartTitle .Characters.Text = "Продажи по месяцам в разрезе сотрудников" .Font.Size = 10 End With В результате контейнер диаграммы получит заголовок, автоматически уменьшив размер собственно диаграммы, чтобы добавленная надпись поместилась в него (рис. 8.26). Зададим далее размер шрифта легенды, … MyChart.Legend.Font.Size = 10

Рис. 8.26. Диаграмма снабжена заголовком

Представление данных в MS Office 219 Ñåìåéñòâî Axes: ôîðìàòèðîâàíèå îñåé äèàãðàììû Число осей зависит от типа диаграммы. У трехмерных диаграмм различают ось значений, ось категорий и ось рядов данных. Все они доступны в семействе Axes соответствующего Chartобъекта, причем требуемая из осей указывается при помощи индекса-константы. Вот, как будет отформатирована ось категорий, то есть, в данном случае, ось месяцев года: With MyChart.Axes(xlCategory) .HasMajorGridlines = True .TickLabels.Font.Size = 8 End With Для оси категорий было разрешено отображение основных линий сетки, а также задан размер шрифта для подписи делений шкалы по этой оси. Затем, аналогичным образом отформатируем ось рядов данных, то есть ту горизонтальную ось, по которой изменяются фамилии сотрудников: With MyChart.Axes(xlSeriesAxis) .HasMajorGridlines = True .TickLabels.Font.Size = 8 End With В результате вид диаграммы снова изменится (рис. 8.27).

Ïðèìåðû ôîðìàòèðîâàíèÿ äèàãðàìì Как еще можно усовершенствовать диаграмму? Тысячей различных способов — объект Chart поистине “многолик”. Простое перечисление свойств диаграммы, поддающихся настройке, заняло бы много места. Не будем даже и пытаться этого делать, просто приведем еще несколько примеров.

Рис. 8.27. Отформатированы оси категорий и рядов данных Ñâîéñòâà HasTitle è AxisTitle: ñîçäàíèå çàãîëîâêîâ îñåé äèàãðàììû Для того чтобы снабдить оси заголовками, необходимо, во-первых, разрешить отображение заголовков (свойство HasTitle), а во-вторых — настроить свойства объекта AxisTitle. Как об-

220 Глава 8. VBA-программирование: примеры и иллюстрации ращаться к различным осям, уже было рассмотрено. Например, снабдим заголовком ось категорий: With MyChart.Axes(xlCategory) .HasTitle = True .AxisTitle.Font.Size = 8 .AxisTitle.Caption = "Месяцы года" End With Для того чтобы выполнить аналогичную операцию с осью рядов данных, необходимо указать индекс этой оси в семействе Axes — это должна быть константа xlSeriesAxis: With MyChart.Axes(xlSeriesAxis) .HasTitle = True .AxisTitle.Font.Size = 8 .AxisTitle.Caption = "Сотрудники" End With Наконец, константа xlValue позволит сделать то же самое с осью значений: With MyChart.Axes(xlValue) .HasTitle = True .AxisTitle.Font.Size = 8 .AxisTitle.Caption = "Продажи" End With В результате в области диаграммы появятся заголовки осей, как показано на рис. 8.28.

Рис. 8.28. Все три оси объемной диаграммы теперь снабжены заголовками Ñâîéñòâà Elevation, Rotation è Perspective: ïîâîðîò äèàãðàììû âîêðóã îñåé Любую трехмерную диаграмму можно поворачивать вокруг любой из осей, меняя углы возвышения (свойство Elevation), поворота (свойство Rotation) и перспективы (свойство Perspective).

Представление данных в MS Office 221

Рис. 8.29. Диаграмма повернулась на различные углы вокруг всех своих осей. Область диаграммы залита текстурой “белый мрамор“. Например, развернем созданную диаграмму следующим образом: MyChart.Elevation = 10 MyChart.Rotation = 120 MyChart.Perspective = 30 Èçìåíåíèå çàëèâêè îáëàñòè äèàãðàììû Для этого необходимо задать для области диаграммы заливку текстурой. Выберем текстуру “белый мрамор”: MyChart.ChartArea.Fill.PresetTextured msoTextureWhiteMarble В результате диаграмма будет повернута в соответствии с заданными углами и ее фон изменится, как показано на рис. 8.29.

Глава 9 Ïðîãðàììèðîâàíèå â Outlook: äîêóìåíòîîáîðîò è ýëåêòðîííàÿ ïî÷òà Ýëåêòðîííàÿ ïî÷òà â ñîâðåìåííîì îôèñå Сегодня Internet-технологии, особенно электронная почта, стали не экзотикой, а привычными и обыденными инструментами бизнеса. Постепенно электронная почта вытесняет факсимильную связь, все уверенней играя роль основного средства обмена информацией как внутри компании, так и за ее пределами. Помимо скорости и качества передачи информации, электронная почта обладает перед факсимильной связью еще одним неоспоримым преимуществом — она не требует для обмена информацией синхронизации действий принимающей и отправляющей сторон. Проще говоря, можно отправить электронную почту адресату, не заботясь о том, включен ли в данный момент его компьютер и находится ли он на своем рабочем месте. Кроме того, электронная почта позволяет сделать массовую рассылку информации, на которую времени уходит практически столько же, сколько и на отправку одного сообщения. Наконец, одним из главных преимуществ электронной почты является ее документированность — в любой момент (если, конечно, были сделаны соответствующие настройки программного обеспечения), можно посмотреть, кому и когда отправлялись письма, найти и повторно распечатать текст письма в случае утери руководителем ранее полученной копии и т.п. Все это так, но возникает вопрос: “Неужели у электронной почты нет недостатков?”. Конечно, недостатки есть — они являются продолжением ее достоинств. Главный из них состоит в том, что со временем поток информации, получаемой и отправляемой по электронной почте, может стать неуправляемым. Если вдруг надо найти какой-то документ, то, как и в случае с другими электронными документами, на решение такой задачи могут уйти многие часы — придется просматривать десятки учетных записей, папок и хранящихся в них писем. Часто такую проблему решают просто, удаляя все ненужные сообщения. Однако это, во-первых, требует ручного управления потоками документов, а во-вторых, может привести к возникновению проблем из-за разных критериев степени важности информации, например, у секретаря и его шефа. Наконец, многие государственные учреждения и организации просто обязаны сохранять всю входящую и исходящую корреспонденцию, в том числе и электронную почту. Где же выход? У читателя данной книги ответ напрашивается сам собой — конечно, в автоматизации документооборота, проходящего по электронной почте. Как нетрудно догадаться, компания Microsoft и здесь предлагает платформу для решения большинства возникающих в этой области задач, которая называется Outlook 2000 и VBA.

Ïîñòàíîâêà çàäà÷è ООО “Универсал” старается идти в ногу с техническим прогрессом. Несколько лет назад руководство компании решило, что настала пора внедрять в работу Internet-технологии, в результате чего ООО “Универсал” подключилось к Internet и начало осваивать электронную почту и World Wide Web. После заключения успешного контракта с крупной немецкой компанией Gans Gmbh, чему немало способствовало наличие связи по электронной почте, руководство убедилось в правильности выбранного курса. Было принято решение о создании собственного Web-сайта, а также о подключении к Internet по выделенному каналу и установке собственного почтового сервера.

224 Глава 9. Программирование в Outlook: документооборот и электронная почта Сотрудники ООО “Универсал” успешно освоили электронную почту и используют ее весьма интенсивно. При этом для удобства менеджеров и специалистов, работающих с клиентами на выезде, основным протоколом электронной почты выбран IMAP4, что позволяет хранить все сообщения на сервере. Рабочие места отдела продаж организованы таким образом, что позволяют менеджерам для работы с электронной почтой использовать любой свободный компьютер или же личный портативный компьютер. Однако руководство компании обеспокоено тем, что процесс получения и отправки почты выходит из-под контроля — ежедневно через каждую из нескольких десятков учетных записей поступают и отправляются десятки сообщений. В результате несколько важных заявок от привлекательных для компании заказчиков из-за болезни менеджера остались без внимания. (К счастью, репутация ООО “Универсал” настолько высока, что потенциальные заказчики оказались настойчивыми и вышли напрямую на руководство компании.) Хотя все завершилось удачно, но, тем не менее, перед группой автоматизации была поставлена задача — обеспечить автоматический учет всех поступающих и отправляемых сообщений по всем учетным записям компании. Система учета должна позволять делать выборку входящих и исходящих сообщений за любой период, а также обеспечить возможность сортировки полученной информации по различным критериям. После изучения поставленной задачи, группа автоматизации пришла к выводу — для представления результатов использовать Excel, а автоматическую работу с электронной почтой построить на основе VBA и Outlook 2000.

Îáúåêòíàÿ ìîäåëü Outlook 2000 Если с автоматизацией с помощью VBA в Excel вопросов практически не возникает, то для работы с Outlook 2000 необходимо немного ознакомится с некоторыми особенностями подходов, принятых компанией Microsoft для решения подобных задач в этой системе. Прежде всего, необходимо отметить, что Outlook 2000 (сюрприз!) не поддерживает функции автоматической записи макросов. Чтобы в этом убедиться, достаточно открыть окно Outlook 2000 и выбрать команду Ñåðâèñ | Ìàêðîñ. Таким образом, для программирования на VBA необходимо хорошо прочесть документацию и другие источники информации, так как на “быстрое программирование”, которое сводится к записе и последующей модификации нужной последовательности команд, рассчитывать не приходится. Второй нюанс заключается в том, что вопросы программирования на VBA в Outlook 2000 документированы поверхностно. По-видимому, это вызвано опасениями компании Microsoft, что их детище будут использовать для написания вирусов. Практика показывает, что “пытливые умы” такие препятствия не останавливают, а вот обычным программистам, занимающимся автоматизацией задач в Microsoft Office, столь странные ограничения доставляют немало лишних проблем. В предыдущих главах уже были рассмотрены теоретические вопросы автоматизации рабочего большинства приложений MS Office, но в данной главе, учитывая упомянутые сложности с программированием Outlook, необходимо познакомится с теорией более глубоко. ← см. также гл. 7 “В мире объектов MS Office”.

Итак, как уже отмечалось, в качестве клиента автоматизации удобнее использовать Excel, а Outlook применять в качестве сервера. Сам термин автоматизация в данном контексте означает технологию, которая позволяет двум независимым программным компонентам обмениваться друг с другом данными. Все приложения Microsoft Office могут выступать как в роли серверов (т.е. приложений, которые предоставляют услуги клиентам), так и в роли клиентов (т.е. приложений, которые пользуются услугами, предоставляемыми серверами автоматизации). В случае автоматизации обмена данными Word и Excel решение о том, кто будет играть роль сервера, а кто — клиента, как правило, несущественно. Однако при подключении к задачам автоматизации Outlook решение о распределении ролей становится важным. Если выбрать в качестве клиента Outlook, а в качестве сервера Excel, разработчик по указанным выше причинам теряет доступ к такому важному инструменту, как макрорекордер. Конечно, можно запустить экземпляр приложения Excel, и

Объектная модель Outlook 2000 225 записывать в нем фрагменты макросов, а затем переносить их в среду интегрированного редактора Visual Basic приложения Outlook Express, но зачем усложнять себе жизнь? ← см. об использовании макрорекордера в разделе “Первый способ формирования счета (использование макрорекордера)” гл. 1.

Êëàññû è îáúåêòû Для того чтобы программировать работу с сервером автоматизации, особенно таким “закрытым”, как Outlook 2000, нужно хорошо понимать, в чем состоит различие между классами и объектами. Классы представляют собой описание типов объектов, которые сервер автоматизации может предоставить в распоряжение клиента. В определении класса описываются присущие ему свойства, методы, а также, в некоторых случаях, события, на которые может реагировать данный класс. Но при написании программного кода все операции выполняются не над классами, а над объектами, представляющими собой конкретные экземпляры реализации того или иного класса. Классы и объекты, в каком-то смысле, соотносятся друг с другом так же, как типы данных и переменные — в VBA имеется ограниченное количество предопределенных типов данных, но программист может создать сколь угодно много экземпляров переменных любого из этих типов. Необходимо отметить, что когда речь идет об объектной модели какого-нибудь приложения, это означает, что имеется в виду набор классов этого приложения. При написании же кода VBA программист имеет дело не с классами, а с реальными объектами.

Áèáëèîòåêè òèïîâ Все основные приложения Microsoft Office 2000 построены в виде компонентов COM. Это означает, что программист может получить информацию об их внутренней структуре, используя соответствующую библиотеку типов. Библиотека типов — это своего рода база данных, в которой хранятся сведения об объектах, методах, свойствах и событиях, поддерживаемых приложением, которое играет роль сервера автоматизации. Именно с помощью библиотеки типов клиент автоматизации может получить нужную ему информацию для обращения к объектной модели сервера. Библиотеки типов могут выполняться как в виде отдельных файлов с расширениями TLB или OLB, так и встраиваться непосредственно в программные файлы приложений, имеющие расширения EXE или DLL. Некоторые библиотеки типов подключаются автоматически, другие же разработчик должен подключать самостоятельно. Можно ли обойтись без использования библиотеки типов? Можно, но тогда для взаимодействия с объектами сервера придется каждый раз запускать соответствующее приложение (в данном случае — Outlook), а интерпретатор VBA не сможет проверять правильность использования синтаксиса при написании программного кода, использующего объекты сервера автоматизации. Кроме того, программист вынужден будет работать “вслепую”, так как у него не будет доступа к интерактивной справочной системе по объектной модели сервера автоматизации.

Êàê ïîäêëþ÷èòü ê Excel áèáëèîòåêó òèïîâ Outlook Запустите Excel и сохраните новый рабочий лист в файле с именем Registrator.xls. Откройте окно редактора Visual Basic командой Сервис | Макрос | Редактор Visual Basic. В окне редактора Visual Basic выберите в меню Tools команду References. Найдите в списке пункт Microsoft Outlook 9.0 Object Library и установите флажок напротив этого пункта (рис. 9.1). 5. Закройте диалоговое окно, щелкнув на кнопке OK.

1. 2. 3. 4.

226 Глава 9. Программирование в Outlook: документооборот и электронная почта

Рис. 9.1. Подключение нужных библиотек типов ← см. также в гл. 2 раздел “Как задать ссылку на библиотеку объектов Word в среде MS Excel”, в гл. 3 раздел “Как задать ссылку на библиотеку объектов Excel в среде MS Word” и в гл. 6 раздел “Ссылка на библиотеку объектов MS Access”.

Подключение библиотеки типов дает возможность разработчику работать с объектами сервера автоматизации точно так же, как и с объектами программируемого им клиента. В частности, из интегрированной среды Microsoft Visual Basic приложения Excel можно получить полный доступ к информации об объектах приложения Outlook. Для этого нужно, находясь в приложении Excel открыть окно Microsoft Visual Basic (это можно сделать несколькими способами, но проще всего нажать комбинацию клавиш [Alt]+[F11]). Если затем в открывшемся окне Visual Basic переключиться в режим просмотра объектов (Object Browser), воспользовавшись для этого командой View | Object Browser или нажав клавишу [F2], можно просматривать информацию обо всех объектах Outlook. При этом можно, щелкнув правой кнопкой мыши на интересующем объекте и выбрав из появившегося меню команду Help, и получить интерактивную справку об этом объекте (рис. 9.2). Таким образом, разработчик, создавая программу для автоматизации клиента Excel, не испытывает никаких проблем с доступом к объектам сервера Outlook.

Объектная модель Outlook 2000 227

Рис. 9.2. Программируя работу Excel, можно иметь под рукой полную информацию по объектам Outlook —это возможно благодаря подключенной библиотеке типов

Íàçíà÷åíèå è îñíîâíûå ñâîéñòâà íåêîòîðûõ êëàññîâ Outlook Если выбрать в окне Object Browser класс Application, открыть окно справки (см. рис. 9.2) и щелкнуть в этом окне на прямоугольнике, представляющем класс Application, откроется полная объектная модель Outlook, которая для удобства приведена на рис. 9.3. Несмотря на кажущееся разнообразие, объектная модель Outlook 2000 довольно проста. Основными элементами этой модели являются: собственно приложение (класс Application), пространство имен (класс NameSpace), синхронизируемые объекты (класс SyncObject), списки адресов (класс AddressList), папки (класс MAPIFolder), иксплореры (класс Explorer), инспекторы (класс Inspector) и элементы, хранящиеся в папках (классы MailItem, NoteItem, JournalItem, MeetingItem и др.). С другой стороны, из довольно ограниченного набора простых объектов разработчики Outlook создали достаточно сложное и мощное приложение. Поэтому для эффективного программирования Outlook необходимо четко представлять назначение, организацию и методы использования всех объектов приложения. Ниже приведено краткое описание назначения и организации основных классов Outlook. Методы практического использования этих классов при создании объектов сервера автоматизации будут рассмотрены по ходу разработки приложения Registrator.

228 Глава 9. Программирование в Outlook: документооборот и электронная почта

Рис. 9.3. Объектная модель Outlook 2000 Êëàññ Application Как уже неоднократно было рассказано в предыдущих главах, для обращения к объектам и другим элементам сервера автоматизации необходимо инициализировать объект класса Application. Это правило справедливо и для Outlook. Самыми важными свойствами объекта класса Application являются: • Explorers — содержит ссылку на коллекцию всех открытых иксплореров. Иксплорер — это объект класса Explorer, предназначенный для отображения содержимого папки Outlook. • Inspectors — содержит ссылку на коллекцию всех открытых инспекторов. Инспектор — это объект класса Inspector, предназначенный для отображения содержимого элемента Outlook (почтового сообщения, контакта и т.п.). • Session — содержит ссылку на активное пространство имен. Пространство имен — это объект класса NameSpace, представляющий собой абстрактный родительский объект, через который обеспечивается доступ ко всем объектам данных Outlook, таких как папки, элементы и т.п. Êëàññ NameSpace Класс NameSpace — это класс, обеспечивающий функцию “проходной” для доступа ко всем папкам и элементам Outlook, причем как иерархического доступа (т.е. с помощью последовательного перемещения по уровням вложенных объектов), так и прямого (по идентификатору объекта). В случае, когда Outlook используется в режиме непосредственной связи с Internet, использование объекта класса NameSpace сводится к простой формальности. Однако при работе с Outlook через сервер Microsoft Exchange Server данный объект выполняет такую важную функцию, как подклю-

Объектная модель Outlook 2000 229 чение к серверу с помощью запроса идентификатора пользователя и пароля. Разработчики Outlook предусмотрели возможность использования пространств имен различных типов, но в версии Outlook 2000 поддерживается только один тип — MAPI. Самыми важными свойствами объекта класса NameSpace являются: • Folders — в данном случае содержит ссылку на коллекцию всех папок уровня пространства имен. Папка — это объект класса MAPIFolder, предназначенный для отображения всех папок, хранящихся на текущем уровне иерархии папок и элементов Outlook. Подробнее объекты класса MAPIFolder будут рассмотрены далее в этой главе. • SyncObjects — содержит ссылку на коллекцию объектов синхронизации. Объектом синхронизации называется объект класса SyncObject, с помощью которого обеспечивается синхронизация локальных папок Outlook с папками Microsoft Exchange Server в определенном режиме, например, ежедневно или раз в неделю. • AddressLists — содержит ссылку на коллекцию адресных книг, доступных в текущем сеансе. Адресная книга представляет собой объект класса AddressList, доступ к которой конкретному пользователю разрешен провайдером этой адресной книги. Êëàññ SyncObject Как уже отмечалось, назначение объектов класса SyncObject— обеспечить синхронизацию определенных папок в определенном режиме. Свойства этого класса малоинтересны, за исключением свойства Name, в котором передается название синхронизируемой группы папок. А вот методы (Start и Stop) и события Progress, SyncEnd и SyncStart достаточно важны для практического использования. Как нетрудно догадаться из их названий, эти методы и события используются для управления процессом синхронизации. Детальнее они будут рассмотрены несколько позже в этой главе. Êëàññ Explorer Класс Explorer предназначен для отображения содержимого папок Outlook. Графический интерфейс объектов этого класса в общем случае напоминает графический интерфейс программы Ïðîâîäíèê (Explorer) Windows. Однако функции объектов класса Explorer Outlook несколько отличаются от функций окна Ïðîâîäíèê Windows (в связи с этим будем называть такие объекты не проводниками, а иксплорерами). Дело в том, что иксплорер позволяет программисту включать и выключать отображение отдельных элементов окна Outlook: в частности, можно отображать по отдельности или в любой комбинации список папок, панель Outlook и область просмотра. Самыми важными свойствами объекта класса Explorer являются: • CurrentFolder — содержит ссылку на объект класса MAPIFolder, представляющий собой текущую открытую папку. • Selection — содержит ссылку на коллекцию всех выделенных элементов, содержащихся в папке. • CommandBars — содержит ссылку на коллекцию, которая представляет собой набор всех команд, доступ к которым в Outlook 2000 можно получить с помощью системы меню и панелей инструментов. Самым интересным свойством этой коллекции является то, что она меняется динамически, в зависимости от выбранного в данный момент элемента. Так, если активной является папка верхнего уровня иерархии, будут доступны одни команды, а если вложенная папка или элемент папки — другие команды. С помощью свойства CommandBars иксплорера можно получить доступ к любой команде меню или панели инструментов Outlook. Среди методов класса Explorer самыми важными являются методы Display и Close. Нетрудно догадаться, что вызов первого метода приводит к открытию окна Outlook (точнее, иксплорера, в котором отображаются определенные программистом компоненты окна Outlook), а второго — к его закрытию. Важность этих методов состоит в том, что они связаны с синхронизацией, что будет рассмотрено несколько позднее.

230 Глава 9. Программирование в Outlook: документооборот и электронная почта Êëàññ MAPIFolder Класс MAPIFolder предназначен для создания доступа к содержимому объектов папок в текущем пространстве имен. Папки могут содержать как другие папки (коллекции Folders), так и элементы Outlook (коллекции Items) либо не содержать ни тех, ни других. Поэтому соответствующие свойства являются самыми важными свойствами объектов класса MAPIFolder. Êëàññ MailItem Объекты класса MailItem представляют один из так называемых элементов Outlook. Элементами могут быть сообщения электронной почты (класс MailItem), контакты (ContactItem), сообщения групп новостей (PostItem), записи журнала (JournalItem) и другие объекты (всего в Outlook 2000 используется 16 классов элементов). Класс MailItem обладает рядом свойств, назначение которых легко понять по их названиям, — во многих случаях они совпадают с названиями соответствующих полей сообщений электронной почты или же подобны им.

Рис. 9.4. Окно программы Outlook Express, предназначенное для управления учетными записями В завершение краткого обзора объектной модели Outlook опишем общую схему использования Outlook в качестве сервера автоматизации. Сначала необходимо создать объект класса Application, затем получить от этого объекта ссылку на пространства имен MAPI. После этого можно, используя иксплореры и инспекторы, просматривать папки сообщений и сами сообщения, а также с помощью соответствующих свойств иксплореров эмулировать выбор команд пользовательского интерфейса Outlook. Кроме того, применяя механизмы работы с коллекциями, можно программным путем обрабатывать папки (объекты класса MAPIFolder) и хранящиеся в них элементы, например, такие как объекты класса MailItem. Теперь, получив некоторые представления об объектной модели Outlook, можно подготовить Outlook к работе в качестве сервера автоматизации.

Создание учетных записей 231

Ñîçäàíèå ó÷åòíûõ çàïèñåé Для того чтобы приложение Outlook 2000 могло работать с электронной почтой, необходимо создать в нем учетные записи. В ООО “Универсал” работают десятки пользователей, но для краткости ограничимся только семью учетными записями — администратора узла, главного инженера, директора, отдела маркетинга, отдела сбыта, начальника ВЦ и общего почтового ящика. Если на компьютере администратора уже имеются учетные записи всех пользователей в программе Outlook Express, проще всего экспортировать их в файлы IAF, а затем импортировать в Outlook 2000. Для этого нужно открыть окно Outlook Express и выбрать из меню команду Ñåðâèñ | Ó÷åòíûå çàïèñè, а затем перейти в открывшемся окне на вкладку Ïî÷òà (рис. 9.4). Для экспортирования выбранной учетной записи в файл нужно щелкнуть на кнопке Ýêñïîðò, а затем в открывшемся окне ввести в поле Èìÿ ôàéëà имя файла IAF, в который будет экспортирована информация из учетной записи, и щелкнуть мышью на кнопке Ñîõðàíèòü (рис. 9.5).

Рис. 9.5. Диалоговое окно Экспортировать учетную запись Экспортировав первую запись (в рассматриваемом примере — администратора), нужно проделать те же операции со второй учетной записью и со всеми остальными учетными записями (рис. 9.6), по которым необходимо отслеживать статистику. Теперь, когда все необходимые учетные записи экспортированы в файлы IAF, нужно проделать обратную операцию — импортировать полученные файлы IAF в приложение Outlook 2000.

Рис. 9.6. Сохраняя следующую учетную запись в файле, в выбранной папке для хранения IAFфайлов можно видеть все ранее сохраненные файлы учетных записей

232 Глава 9. Программирование в Outlook: документооборот и электронная почта Необходимо отметить, что Outlook может импортировать информацию из установленных почтовых клиентов автоматически, не прибегая к экспорту и импорту файлов IAF при первом запуске программы на компьютере после установки.

Рис. 9.7. Окно управления учетными записями Outlook 2000 — это тот же объект, который используется для этих целей в Outlook Express Однако так как в ООО “Универсал” был выбран (как всегда) не самый простой, но зато универсальный (а значит “фирменный”) способ, будем рассматривать именно его. Операция импортирования учетных записей из файлов IAF в Outlook 2000 сводится к использованию все того же окна Ó÷åòíûå çàïèñè â Èíòåðíåòå. ÏÐÈÌÅ×ÀÍÈÅ

Кстати, окно в буквальном смысле то же самое, так как соответствующий компонент является независимым объектом, который используют и программа Outlook Express, и Outlook 2000. Именно по этой причине язык, используемый в этом окне, может отличаться от языка, используемого в приложении, если Outlook Express и Outlook 2000 используют разные языки пользовательского интерфейса. Для открытия окна управления учетными записями Outlook 2000 необходимо, запустив приложение, выбрать из меню команду Ñåðâèñ | Ó÷åòíûå çàïèñè, а затем перейти в открывшемся окне на вкладку Ïî÷òà (рис. 9.7).

Создание учетных записей 233

Рис. 9.8. Окно управления учетными записями Outlook 2000 — это тот же объект, который используется для этих целей в Outlook Express

Рис. 9.9. Набор учетных записей Outlook Express успешно импортирован в Outlook 2000 Щелкнув на кнопке Èìïîðò, нужно выбрать в открывшемся окне необходимую учетную запись (рис. 9.8), а затем щелкнуть на кнопке Îòêðûòü — учетная запись появится в окне управления учетными записями Outlook. К сожалению, окно управления учетными записями не позволяет экспортировать и импортировать их группами, поэтому приходится выполнять данные операции с каждой учетной записью по отдельности. Проделав подобную операцию для всех файлов IAF, получим набор учетных записей пользователей электронной почты, аналогичный набору Outlook Express (рис. 9.9). При этом нет необходимости вручную определять все параметры подключения, а также задавать пароли. Конечно это справедливо, при условии, что у всех учетных записей пользователей установлен флажок Çàïîìíèòü ïàðîëü на вкладке Ñåðâåð окна свойств учетной записи. После закрытия окна управления учетными записями окно Outlook 2000 приобретете вид, подобный рис. 9.10 (при необходимости система предложит загрузить информацию из папок учетных записей сервера).

234 Глава 9. Программирование в Outlook: документооборот и электронная почта

Рис. 9.10. Учетные записи представлены в виде папок IMAP4, загружаемых с сервера ÏÐÈÌÅ×ÀÍÈÅ

Создать несуществующую учетную запись можно используя Ìàñòåð ïîäêëþ÷åíèÿ Èíòåðíåò. Для его запуска можно раскрыть в Outlook диалоговое окно Ó÷åòíûå çàïèñè â Èíòåðíåò и перейти на вкладку Ïî÷òà. Затем щелкнуть на кнопке Äîáàâèòü и выбрать в раскрывшемся меню пункт Ïî÷òà.

Рис. 9.11. Установите размер окна Outlook 2000 таким, чтобы в области просмотра отображалось несколько сообщений любой учетной записи Теперь осталось лишь уменьшить окно Outlook 2000 до приемлемых размеров (рис. 9.11) и закрыть его. Размер окна, конечно, можно задавать и программно, но установка его размеров вручную позволит не загромождать создаваемое приложение вспомогательным кодом, а сосредоточится на его ключевых моментах. Изменение размера окна станет понятно чуть позже.

Ñîçäàíèå ïðèëîæåíèÿ Registrator Завершив подготовку Outlook 2000, можно приступать собственно к разработке приложения Registrator. Разработка приложения будет проводиться в следующей последовательности: • создание заготовки приложения; • определение констант, переменных, классов и форм; • разработка схемы приложения; • написание основного и вспомогательного программного кода; • доводка и совершенствование приложения.

Создание приложения Registrator 235

Ñîçäàíèå çàãîòîâêè ïðèëîæåíèÿ Создание заготовки приложения начнем с подготовки рабочего листа.

Èçìåíåíèå èìåíè ëèñòà Откройте созданный в начале главы файл Registrator.xls, переименуйте его первый лист в Ïàíåëü óïðàâëåíèÿ, удалите все остальные листы (рис. 9.12) и сохраните полученную рабочую книгу, состоящую из одного листа, на диске.

Рис. 9.12. Так выглядит рабочий лист Панель управления приложения Registrator

Ñîçäàíèå ýëåìåíòà óïðàâëåíèÿ Êíîïêà Создадим элемент управления, предназначенный для обеспечения запуска разрабатываемого приложения пользователем из рабочего листа Ïàíåëü óïðàâëåíèÿ. Для этого нужно отобразить на экране панель инструментов Ôîðìû, выбрать из этой панели объект Êíîïêà и нарисовать кнопку на рабочем листе. ← см. также в гл. 1 раздел “Как поместить на лист элемент управления”.

Ñîçäàíèå çàãîòîâêè ìàêðîñà, ñâÿçàííîãî ñ êíîïêîé После того, как будет отпущена кнопка мыши, Excel создаст кнопку Êíîïêà 1, размер которой будет соответствовать размеру очерченной области, и откроет окно назначения кнопке макроса. Щелкните в этом окне на кнопке Ñîçäàòü. Это приведет к запуску редактора Visual Basic, созданию нового модуля и заготовки процедуры со стандартным именем Кнопка1_Щелкнуть. Измените имя этой процедуры на cbGet_IMAP (листинг 9.1), и переименуйте имя модуля Module1 на main. ËÈÑÒÈÍÃ 9.1

Sub cbGet_IMAP() Sub End

Ñîçäàíèå ìîäóëåé, êëàññîâ è ôîðì, è èçìåíåíèå èõ ñâîéñòâ Для изменения стандартного имени модуля нужно воспользоваться окном свойств Properties (команда View | Properties Windows, клавиша [F4]) и ввести в качестве значения свойства (Name) соответствующее название, как показано на рис. 9.13. На исходный код, приведенный на этом рисунке пока не обращайте внимания — вернемся к нему позднее.

236 Глава 9. Программирование в Outlook: документооборот и электронная почта Также создайте в окне Visual Basic модуль с именем misc (команда Insert | Module), модуль класса mySync (Insert | Class Module) и формы Form1 (Insert | UserForm).

Рис. 9.13. Проект приложения Registrator с двумя созданными модулями, одним модулем класса и одной формой. В правой части окна показаны объявления переменных объектов и переменных уровня всего проекта и уровня модуля main Создав заготовки модулей, откройте форму Form1 и поместите на нее метку (объект класса Label, которому система автоматически присвоит имя Label1). Установите свойства формы следующим образом: (Name)

Form1

Caption Height Width

Подождите… 83 192

Затем задайте указанные ниже значения для следующих свойств метки: Caption

Идет синхронизация

Font

Tahoma

ForeColor Height Left TextAlign Top Width

&H000000FF& 12 36 2-fmTextAlignCenter 24 132

полужирный красный

Форма приобретет вид, показанный на рис. 9.14.

Создание приложения Registrator 237

Рис. 9.14. Форма Form1 с размещенной на ней меткой

Èçìåíåíèå ñâîéñòâ êíîïêè íà ðàáî÷åì ëèñòå Excel Для того чтобы изменить текст, отображаемый на созданной кнопке на рабочем листе Excel, нужно вернуться на этот лист, например, закрыв панель инструментов Ôîðìû. Затем щелкнуть на названии кнопки мышью — появится курсор ввода, и отредактировать текст, изменив его на название Ïðîâåñòè ðåãèñòðàöèþ, а затем нажать дважды Esc. Осталось лишь закрыть и проверить работу кнопки. Если подвести к ней указатель мыши, он примет форму указывающей руки. Это говорит о том, что кнопке назначен макрос, который будет выполнен, если щелкнуть на ней. Щелкнув на кнопке, можно увидеть… да, ровным счетом ничего не увидеть! Объяснения излишни — назначенный кнопке макрос cbGet_IMAP, находящийся в модуле main, ничего не выполняет. На этом этап создания заготовки программы закончен — сохраните файл Registrator.xls. Теперь перейдем к следующему этапу, состоящему в определении констант, переменных, классов и форм. ÑÎÂÅÒ

Чтобы быстро переключаться между окном рабочей среды Excel и окном редактора Visual Basic можно воспользоваться Windows-сочетанием клавиш [Alt]+[Tab], или щелкать мышью на соответствующих им пиктограммах на панели задач Windows.

Îïðåäåëåíèå ïðîãðàììíûõ ýëåìåíòîâ Для начала перейдите в окно Visual Basic, откройте модуль main и введите в окне редактора объявления элементов программы, показанные в листинге 9.2. По окончанию ввода окно редактора должно иметь вид, подобный показанному на рис. 9.13.

238 Глава 9. Программирование в Outlook: документооборот и электронная почта ËÈÑÒÈÍÃ 9.2

Public objOutlook As Outlook.Application Public myForm As New Form1 '****************************************** '*** Объекты Outlook *** '****************************************** Private Private Private Private Private Private Private Private Private

FolderList As Outlook.Folders CurNameSpace As Outlook.NameSpace CurFolder As Outlook.MAPIFolder CurMsg As Outlook.MailItem CurExplorer As Outlook.Explorer CurSync As New mySync Account As Outlook.SyncObject FolderItem As Object Attach As Outlook.Attachment

'********************************************* '*** Переменные модуля *** '********************************************* Private Private Private Private

LastInputRow As String InputDataRange, OutputDataRange As String Interval As Variant DateStart, DateEnd As Variant

Затем откройте модуль misc и введите в окне редактора объявления элементов программы, показанные в листинге 9.3. ËÈÑÒÈÍÃ 9.3

Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public Public

Const sRow As String = "R", sCol As String = "C" Const sHome As String = "1", sStart As String = "0" Const sFirstCol As String = "A" Const sLastCol As String = "F" Const sTimeKey As String = "D3" Const sSemicolon As String = ":" Const sDollar As String = "$" Const sDot As String = "." Const sSpace As String = " " Const sHiphen As String = "-" Const sAddressCol As String = "$A$2" Const sSubjectCol As String = "$E$2" Const sAttachmentsCol As String = "$C$2" Const iGoodWidth As Integer = 30 Const iGoodHeight As Integer = 25 Const iGoodAttachWidth As Integer = 100 Const sMorningMail As String = "09:00:00" Const sEveningMail As String = "14:00:00" Const sInbox As String = "Входящие" Const sSentItems As String = "Sent Items" Const iTopHeader As Integer = 1 Const iBottomHeader As Integer = 2 MaxAttachmentsLen As Integer

Создание приложения Registrator 239 Наконец откройте окно модуля класса mySync и введите в окне редактора объявление, показанное в листинге 9.4. ËÈÑÒÈÍÃ 9.4

Dim WithEvents mySync As Outlook.SyncObject  см. также в приложении раздел “Область видимости и время жизни переменных ”.

Сохраните файл Registrator.xls. Теперь немного остановимся и рассмотрим только что добавленные объявления объектов, переменных, констант и других элементов программы.

Àíàëèç îáúÿâëåííûõ ïðîãðàììíûõ ýëåìåíòîâ Основную роль в создаваемой программе играют объявления, представленные в листинге 9.2. Как видно из листинга, здесь объявляется объект класса objOutlook, представляющий собой экземпляр класса Outlook.Application. Именно этот объект и играет роль сервера автоматизации — будем его инициализировать в приложении, обращаться к его методам, объектам и свойствам, а затем завершать с ним работу. Помимо этого главного объекта, объявлены также объекты, предназначенные для работы с пространством имен (CurNameSpace), списком учетных записей IMAP, представленных в виде папок верхнего уровня (FolderList), текущей папкой (CurFolder), текущим сообщением электронной почты (CurMsg), текущим иксплорером (CurExplorer) и текущим объектом синхронизации (Account). Последний объект необходимо рассмотреть подробнее. Как видно из листингов 9.2 и 9.4, в программе объявлен объект синхронизации Account и родственный ему объект синхронизации CurSync, представляющий собой объект класса mySync. Класс mySync, объявление которого (см. листинг 9.4) содержится в модуле класса mySync, представляет собой расширенную версию класса SyncObject. Зачем нужны такие сложности? Объект Account предназначен для получения ссылки на очередной объект синхронизации, соответствующий определенной учетной записи. С использованием этой ссылки будет активизироваться объект CurSync, имеющий функциональность, которая расширяет стандартную функциональность объектов класса SyncObject с целью обеспечения наглядного отображения хода синхронизации с использованием формы myForm. Пока что это звучит несколько непонятно, но на данном этапе этого вполне достаточно — впоследствии механизм программно управляемой синхронизации будет рассмотрен более подробно. Объекты FolderItem и Attach носят вспомогательный характер и предназначены для обеспечения работы с элементами, хранящимися в папках, и вложениями сообщений электронной почты, соответственно. Большое значение также имеет переменная myForm, которая представляет собой объект класса Form1. Данный класс был разработан на первом этапе. Объект myForm предназначен для совместной работы с классом mySync. Задача этого объекта — обеспечить отображение на экране формы до тех пор, пока не будет закончена синхронизация очередной учетной записи с выводом на экран информации о ходе синхронизации. Остальные переменные модуля main, равно как и переменные и константы модуля misc, носят вспомогательный характер. Об их назначение можно узнать позднее, когда будет разбираться исходный код соответствующих процедур и функций. На этом этап объявления элементов программы закончен. Сохраните файл Registrator.xls, откройте модуль main и поместите курсор ввода в процедуру cbGet_IMAP.

Ðàçðàáîòêà ñõåìû ïðèëîæåíèÿ Проведенную в предыдущих разделах подготовительную работу, строго говоря, можно было бы не выполнять. Как легко догадаться, только самые опытные программисты могут сразу, до начала разработки приложения, определить все необходимые им константы, объекты, переменные, классы и т.п. Однако в этом примере была проявлена неслыханная мудрость и прозорливость, все элементы программы были определены на начальном этапе. Это сделано умышленно, чтобы мож-

240 Глава 9. Программирование в Outlook: документооборот и электронная почта но было сосредоточиться на основной задаче этой главы, и не тратили время на разработку кнопок, добавление переменных, объектов и т.д. Теперь можно заняться и собственно приложением. Учитывая требования, выдвигаемые к нему постановкой задачи, а также необходимость предоставления результатов в удобочитаемом виде (т.е. в виде отформатированного отчета), можно определить схему работы приложения в следующем виде. 1. Инициализация переменных. 2. Синхронизация (при необходимости) локальных папок с папками сервера. 3. Запрос у пользователя периода, за который нужно выбрать сообщения. 4. Создание нового рабочего листа Excel, на котором будут регистрироваться сообщения. 5. Подготовка заголовка таблицы для входящих сообщений. 6. Выборка входящих сообщений из локальных папок. 7. Определение адреса диапазона, в котором находятся записи зарегистрированных входящих сообщений. 8. Подготовка заголовка таблицы для исходящих сообщений. 9. Выборка исходящих сообщений из локальных папок. 10. Выгрузка из памяти приложения Outlook. 11. Определение адреса диапазона, в котором находятся записи зарегистрированных исходящих сообщений. 12. Окончательное форматирование отчета и завершение работы приложения. Как видите, схема достаточно проста и понятна. Вопросы могут возникнуть лишь по некоторым пунктам. Необходимость наличия п. 2 объясняется тем, что, во-первых, Outlook работает не с папками IMAP, хранящимися на сервере, а с локальными папками, которые автоматически синхронизируются с сервером при создании или удалении каких-либо элементов. Поэтому для того, чтобы перед выборкой сообщений всегда иметь самые свежие данные, нужно провести синхронизацию. Однако в некоторых случаях в данной операции нет необходимости — например, если выполнять выборку сообщений за прошлый месяц, а синхронизация папок осуществлялась всего лишь несколько часов назад. В этой связи операция синхронизации должна выполняться по решению пользователя. Необходимость получения отчета по всем без исключения отправленным и полученным сообщениям возникает крайне редко. В этой связи имеет смысл обеспечить механизм определения какого-то определенного интервала, за который нужно провести выборку информации. Именно для этих целей в схему введен п. 3. Это позволит пользователю определить границы интервала, в зависимости от стоящих перед ним задач. Наконец, пп. 7 и 11 предназначены для обеспечения форматирования отчета в п. 12. Действительно, количество сообщений может меняться, поэтому приложение должно корректно определять границы диапазона ячеек рабочего листа Excel, в котором находятся сведения о полученных или об отправленных письмах, а также корректно обрабатывать ситуацию, когда входящих или (и) исходящих писем за заданный период не обнаружено. Теперь откройте модуль main и поместите в тело процедуры cbGet_IMAP программный код, представленный в листинге 9.5, а после оператора выхода из процедуры — код, представленный в листинге 9.6. ËÈÑÒÈÍÃ 9.5

'*** Главная процедура программы. '*** 1. Инициализация переменных. Initialization '*** 2. Синхронизация локальных папок с папками сервера. Synchronization

Создание приложения Registrator 241 '*** 3. Определение интервала, за который нужно выбрать сообщения. Interval = CalculateInterval(DateStart, DateEnd) '*** 4. Создание нового листа для регистрации сообщений из '*** заданного интервала. MakeNewReport DateStart, DateEnd '*** 5. Подготовка заголовка таблицы для входящих сообщений. MakeReportHeader sInbox, _ "Входящие сообщения, полученные за период с " _ & DateStart & " по " & DateEnd '*** 6. Выборка входящих сообщений из локальных папок. ProcessFolder (sInbox) '*** 7. Определение адреса диапазона, в котором находятся '*** записи зарегистрированных входящих сообщений. CalculateInputRange '*** 8. Подготовка заголовка таблицы для исходящих сообщений. MakeReportHeader sSentItems, _ "Исходящие сообщения, отправленные за период с " _ & DateStart & " по " & DateEnd '*** 9. Выборка входящих сообщений из локальной папки. ProcessFolder (sSentItems) '*** 10. Выгрузка из памяти приложения Outlook. Deinitialization '*** 11. Определение адреса диапазона, в котором находятся '*** записи зарегистрированных исходящих сообщений. CalculateOutputRange '*** 12. Оформление отчета. Application.StatusBar = "Оформление отчета..." FormatReport (InputDataRange) FormatReport (OutputDataRange) Application.StatusBar = "Макрос выполнен успешно." ËÈÑÒÈÍÃ 9.6

Sub ProcessFolder(FolderType As String) End Sub Sub Synchronization() End Sub Sub CalculateInputRange() End Sub Sub CalculateOutputRange()

242 Глава 9. Программирование в Outlook: документооборот и электронная почта End Sub Sub Initialization() End Sub Sub Deinitialization() End Sub Как видно из листингов 9.5 и 9.6, некоторые процедуры и функции остались неопределенными. Так как они по большей части носят вспомогательный характер, их рациональнее поместить в модуль misc. Откройте модуль misc и поместите после объявления переменных и констант программный код, представленный в листинге 9.7. ËÈÑÒÈÍÃ 9.7

Sub MakeNewReport(sStartDate As Variant, sEndDate As Variant) End Sub Sub MakeReportHeader(sFolderType As String, _ ByVal sHeaderText As String) End Sub Sub FormatReport(Selector As String) End Sub Function CalculateInterval(sStartDate, sEndDate As Variant) As Variant End Function

Êîìïèëÿöèÿ ïðîåêòà è çàïóñê åãî íà âûïîëíåíèå Выберите из меню команду Debug | Compile VBAProject для компиляции проекта. Если текст введен без ошибок, Visual Basic “молчаливо одобрит” это творение, после чего команда Compile VBAProject будет оставаться недоступной до тех пора пока не будут внесены такие изменения в исходный текст, которые потребуют перекомпиляции проекта. Скомпилированный проект можно запустить на выполнение с помощью команды пользовательского интерфейса Visual Basic Run | Run Sub/UserForm. Однако так как была предусмотрена специальная кнопка для запуска приложения из рабочего листа Ïàíåëü óïðàâëåíèÿ, воспользуемся именно этим методом. Перейдите в окно Microsoft Excel и щелкните на созданной кнопке Ïðîâåñòè ðåãèñòðàöèþ. Ву-а-ля! В этот раз почти ничего не произошло. “Почти”, так как кое-что все-таки изменилось: в строке состояния вместо стандартной надписи ”Готово” появилась надпись ”Макрос выполнен успешно”. Это означает, что созданный “костяк” приложения работает. Сохраните файл Registrator.xls, перейдите в окно Visual Basic и снова откройте модуль main. Осталось совсем немного — реализовать данный алгоритм в виде программного кода. Это и будет сделано в следующем разделе.

Создание приложения Registrator 243

Íàïèñàíèå ïðîãðàììíîãî êîäà Èíèöèàëèçàöèÿ è äåèíèöèàëèçàöèÿ Прежде всего, создадим программный код процедур инициализации и деинициализации. Для этого поместите в процедуры Initialization и Deinitialization операторы, приведенные в листинге 9.8. ËÈÑÒÈÍÃ 9.8

Sub Initialization() Set objOutlook = New Outlook.Application Set CurNameSpace = objOutlook.GetNamespace("MAPI") Set FolderList = CurNameSpace.Folders Application.StatusBar = "" MaxAttachmentsLen = 0 End Sub Sub Deinitialization() objOutlook.Quit Set objOutlook = Nothing End Sub В процедуре Initialization создается новый объект objOutlook класса Outlook.Application. Понятно, что это ключевой объект рассматриваемого здесь приложения, играющий роль сервера автоматизации. Затем в переменную CurNameSpace помещается ссылка на пространство имен MAPI сервера автоматизации. Это обеспечивает доступ ко всем папкам и элементам инициализированного экземпляра сервера автоматизации. В третьей строке процедуры содержится оператор инициализации переменной FolderList, которая представляет собой ссылка на объект Folders текущего пространства имен. Данный объект есть ни что иное, как перечень папок верхнего уровня, соответствующих учетным записям пользователей на почтовом сервере. Смысл следующего оператора понятен — он очищает строку состояния. Наконец, последний оператор явно обнуляет значение вспомогательной глобальной переменной, которая будет использована при обработке сообщений с вложениями. Назначение процедуры Deinitialization, как уже было установлено, прямо противоположно назначению первой процедуры. В процедуре Deinitialization завершается работа сервера автоматизации с очисткой соответствующей объектной переменной. Остальные объектные переменные, инициализированные в начале программы, в явной форме очищать не обязательно, так как они очищаются автоматически при очистке объекта-владельца. Выполните “прогон программы”, чтобы убедиться в том, что все работает нормально. В это раз он должен происходить несколько медленнее, чем вначале (это хорошо видно по строке состояния, которая в течение какого-то времени остается пустой). Это и понятно — приложение Outlook 2000 довольно объемное. Сохраните проект, и будем двигаться дальше.

Ñèíõðîíèçàöèÿ Для того чтобы получить информацию от сервера автоматизации, роль которого играет Outlook 2000, необходимо хотя бы один раз синхронизировать локальные папки последнего с папками IMAP, хранящимися на почтовом сервере. Таким образом, нужно создать процедуру синхронизации. Однако сначала придется создать вспомогательный класс mySync, который обеспечит, вопервых, визуализацию процесса синхронизации, а во-вторых (что более важно) — остановку приложения до тех пор, пока не будут синхронизированы все элементы текущего объекта синхронизации. Действительно, одни папки могут содержать лишь несколько сообщений, и для их синхронизации достаточно будет долей секунды, тогда как на синхронизацию других, содержащих де-

244 Глава 9. Программирование в Outlook: документооборот и электронная почта сятки и сотни сообщений, может уйти несколько минут. Так как процессы обращения к почтовому серверу и работы приложения VBA выполняются на разных, не связанных друг с другом уровнях, возникает задача обеспечения приостановки работы приложения до завершения синхронизации. Решить эту проблему можно с помощью классов mySync и Form1. Объект класса mySync обеспечивает отображение модальной формы myForm, представляющей собой объект класса Form1, а также запуск и отслеживание синхронизации с выдачей сигнала о ее завершении. Так как форма myForm является модальной, работа приложения будет приостановлена до тех пор, пока данная форма не будет закрыта. Закрытие же формы выполняется обработчиком события класса mySync, выполняющимся после получения сигнала о завершении синхронизации. С помощью такого изящного механизма и обеспечивается взаимодействие двух асинхронных процессов — процесса обновления локальных папок Outlook и процесса выполнения приложения VBA. Так как класс формы Form1 и модуль класса mySync были созданы на этапе подготовки приложения, осталось лишь написать собственно код класса, а также код, обеспечивающий его использование. Для того чтобы подготовить код класса mySync, перейдите в соответствующий модуль и добавьте к строке объявления класса операторы, показанные в листинге 9.9. ËÈÑÒÈÍÃ 9.9

Public Sub Start() mySync.Start myForm.Show End Sub Sub Init(AnItem As String) Set mySync = objOutlook.Session.SyncObjects(AnItem) End Sub Private Sub mySync_SyncStart() myForm.Caption = "Подождите..." End Sub Private Sub mySync_Progress(ByVal State As Outlook.OlSyncState, ByVal Description As String, ByVal Value As Long, ByVal Max As Long) Cap = Str(Value / Max * 100) & "% " myForm.Label1.Caption = "Идет синхронизация: " & Cap End Sub

_ _

Private Sub mySync_SyncEnd() myForm.Hide End Sub Назначение операторов понятно и не требует комментариев. Данные процедуры представляют собой обработчики событий SyncStart, SyncEnd и Progress. Первый и второй обработчики выполняются при поступлении событий начала и окончания синхронизации, а последний — периодически по ходу синхронизации. Событие SyncStart необходимо инициировать программно, тогда, как событие SyncEnd может генерироваться системой при окончании синхронизации. Эти свойства класса SyncObject и будут использованы, чтобы создать собственный класс mySync, предназначенный для открытия, обновления и закрытия обработчиками событий формы myForm класса Form1. Теперь вернемся к модулю main и добавим в процедуру Syncronization код, показанный в листинге 9.10. ËÈÑÒÈÍÃ 9.10

Dim Responce As Integer

Создание приложения Registrator 245 Responce = MsgBox("Выполнять синхронизацию?", _ vbYesNo, "Макрос запущен") If Responce = vbYes Then For Each Account In objOutlook.Session.SyncObjects Application.StatusBar = "Учетная запись: " & Account CurSync.Init (Account) CurSync.Start Next Account End If Как вы помните, Account — это объект класса SyncObject. В данном фрагменте программного кода выполняется последовательный перебор всех объектов синхронизации, хранящихся в коллекции SyncObjects текущего сеанса сервера автоматизации objOutlook. Ссылка на очередной объект синхронизации передается методу Init объекта CurSync класса mySync. Возвратясь к листингу 9.9, можно увидеть, что данный метод инициализирует экземпляр класса mySync, вследствие чего при вызове метода CurSync.Start управление передается не стандартному обработчику события SyncStart класса SyncObject, а созданному аналогичному обработчику класса mySync. Сохраните проект и попробуйте выполнить его с помощью кнопки Ïðîâåñòè ðåãèñòðàöèþ, находящейся на рабочем листе Ïàíåëü óïðàâëåíèÿ. Если все сделано правильно, сначала появится окно сообщения, предлагающее подтвердить необходимость регистрации, а затем, если будет выбран утвердительный ответ, на экране появится окно, показанное на рис. 9.15.

Рис. 9.15. На созданной форме отображаются сведения о ходе процесса синхронизации, а в строке состояния — сведения о текущей учетной записи

246 Глава 9. Программирование в Outlook: документооборот и электронная почта Казалось бы, на этом можно было бы остановиться, считая, что задача синхронизации решена. Однако в действительности это может оказаться не совсем так. Дело в том, что в некоторых случаях указанным выше методом можно синхронизовать только папку входящих сообщений, а другие папки Outlook почему-то игнорирует. Для того чтобы заставить “капризное дитя Microsoft” работать, как положено, можно применить следующий прием (листинг 9.11, изменения процедуры синхронизации выделены полужирным шрифтом). ËÈÑÒÈÍÃ 9.11

Dim Responce As Integer Responce = MsgBox("Выполнять синхронизацию?", _ vbYesNo, "Макрос запущен") If Responce = vbYes Then For Each Account In objOutlook.Session.SyncObjects Application.StatusBar = "Учетная запись: " & Account Set CurExplorer = _ FolderList(Account.Name).Folders(sSentItems).GetExplorer CurExplorer.Display Application.Wait (Now + TimeValue("0:00:05")) CurExplorer.Close Set CurExplorer = Nothing CurSync.Init (Account) CurSync.Start Next Account End If Теперь после получения ссылки на очередную синхронизируемую учетную запись имя этой учетной записи, а также название нужной папки (в данном случае — папки исходящих сообщений, см. листинг 9.3) используется для вызова метода GetExplorer коллекции Folders объекта FolderList. Этот метод возвращает ссылку на иксплорер, с помощью которого можно просмотреть содержимое заданной папки указанной учетной записи. Фокус заключается в том, что если задержать выполнение приложения на несколько секунд, включится внутренний механизм Outlook 2000, и папка будет синхронизирована автоматически, что легко увидеть, открыв окно иксплорера. Именно эту задачу и выполняют добавленные операторы — сначала будет получена ссылка на нужный иксплорер, затем он будет отображен для наглядности, после чего приостанавлено выполнение приложения на 5 с, а затем закрыт иксплорер и очищана соответствующая переменная. Два последних оператора важны, — если имеется активный иксплорер, то при начале синхронизации с помощью объекта CurSync начнется параллельная синхронизация учетной записи иксплорером, что может привести к неустойчивой работе и зависанию приложения. Сохраните проект и снова проверьте, как он работает. Теперь на экране поочередно должны появлятся то диалоговое окно синхронизации, то окно Outlook 2000 (т.е. окно текущего иксплорера, показывающего содержимое папки исходящих сообщений очередной учетной записи). Если в папках исходящих сообщений были несинхронизированные записи, они появятся в области просмотра содержимого папки в правой части окна иксплорера. Теперь можно считать, что с синхронизацией порядок наведен. В принципе, если вас устраивает полученный набор почтовых сообщений, в дальнейшем можете просто отвечать отрицательно на вопрос приложения, нужно ли выполнять синхронизацию, — это позволит сэкономить время на тестовых “прогонах программы”.

Создание приложения Registrator 247

Îïðåäåëåíèå íóæíîãî èíòåðâàëà è ñîçäàíèå íîâîãî ëèñòà Приведенные в этом разделе две процедуры носят вспомогательный характер, поэтому объединены в одном разделе. Их код можно реализовать по-разному, например, так, как показано в листингах 9.12 и 9.13. ËÈÑÒÈÍÃ 9.12

Function CalculateInterval(sStartDate, sEndDate As Variant) As Variant Dim sNow, sNowTime As String Dim DefaultDateValue, DefaultDateStart, DefaultDateEnd As Variant CalculateInterval = 0 sNow = Now: sNowTime = PadTime(Now) If sNowTime < sEveningMail Then '*** Время получения вечерней почты еще не настало -'*** получаем почту за предыдущую ночь (или выходные). If Weekday(sNow) = vbMonday Then DefaultDateValue = DateValue(DateAdd("y", -3, sNow)) Else DefaultDateValue = DateValue(DateAdd("y", -1, sNow)) End If DefaultDateStart = DefaultDateValue & " " & sEveningMail DefaultDateEnd = DateValue(sNow) & " " & sMorningMail Else '*** Время получения вечерней почты уже настало -'*** получаем почту за сегодняшний день DefaultDateStart = DateValue(sNow) & " " & sMorningMail DefaultDateEnd = DateValue(sNow) & " " & sEveningMail End If sStartDate = GetDate("НАЧАЛА", DefaultDateStart) sEndDate = GetDate("ОКОНЧАНИЯ", DefaultDateEnd) CalculateInterval = DateDiff("s", sStartDate, sEndDate) End Function Function PadTime(TheDate As Variant) As String Dim sTime As String sTime = TimeValue(TheDate) If Len(sTime) < 8 Then sTime = "0" & sTime PadTime = sTime End Function Function GetDate(Prompt As String, Default As Variant) As Variant Dim TheDate As String Do TheDate = InputBox("Дата и время " & Prompt & _ " периода, за который необходимо получить почту", _ "Определение периода получения почты", Default) If Not IsDate(TheDate) Then MsgBox "Дата " & TheDate & _ " указана неверно!", vbCritical Loop While Not IsDate(TheDate) GetDate = TheDate End Function ËÈÑÒÈÍÃ 9.13

Sub MakeNewReport(sStartDate As Variant, sEndDate As Variant)

248 Глава 9. Программирование в Outlook: документооборот и электронная почта Dim SheetName As Variant Dim Found As Boolean Dim NewSheet, ControlPanel As Worksheet Set ControlPanel = Sheets.Item("Панель управления") Found = False SheetName = Left(DateValue(sStartDate), 6) & _ Right(DateValue(sStartDate), 2) & _ sSpace & _ Left(PadTime(TimeValue(sStartDate)), 2) & _ sDot & _ Mid(PadTime(TimeValue(sStartDate)), 4, 2) & _ sHiphen & _ Left(DateValue(sEndDate), 6) & _ Right(DateValue(sEndDate), 2) & _ sSpace & _ Left(PadTime(TimeValue(sEndDate)), 2) & _ sDot & _ Mid(PadTime(TimeValue(sEndDate)), 4, 2) For i = 1 To Sheets.Count If Sheets.Item(i).Name = SheetName Then MsgBox "Лист записей за период с " & sStartDate & _ " по " & sEndDate & " уже существует." Set NewSheet = Sheets.Item(i) Found = True Exit For End If Next i If Not Found Then Set NewSheet = Sheets.Add NewSheet.Name = SheetName NewSheet.Move After:=ControlPanel End If NewSheet.Select: End Sub

GoNext (sStart)

Как легко заметить, в листинге 9.12 приведена не только функция CalculateInterval, возвращающая значение интервала между двумя заданными датами, выраженное в секундах, а также изменяющая значения глобальных переменных DateStart и DateEnd (см. листинг 9.2), — в нем также содержатся вспомогательные функции PadTime и GetDate. Первая из них предназначена для дополнения слева строкового представления времени символом незначащего нуля, а вторая — для организации диалога с пользователем и запроса у него ввода даты с проверкой корректности введенного значения. Две последние функции просты, а их работу легко понять из анализа их исходного текста. Функция же CalculateInterval имеет две “изюминки”. Во-первых, она помогает пользователю, формируя для него дату и время начала (окончания) интервала, устанавливаемые в окне запроса по умолчанию. Так, если пользователь выполняет регистрацию утром в понедельник, функция автоматически предложит ему провести регистрацию всех писем, поступивших за выходные, а также во второй половине дня в пятницу. Если же он запустит регистрацию в понедельник вечером, функция предложит провести регистрацию только писем, поступивших за текущий

Создание приложения Registrator 249 день. При этом пользователь может отказаться от предложенных ему по умолчанию значений даты и (или) времени, и ввести другие значения — например, указать, чтобы программа выполнила регистрацию всех писем за прошлый месяц. Во-вторых, интервал вычисляется как разность между двумя датами, выраженная в секундах. Это вычисление производится простой полезной функцией DateDiff, которая будет рассмотрена несколько позже. Что касается функции MakeNewReport (листинг 9.13), то она также проста и не требует особых комментариев. Создаваемый в ней новый лист отчета помещается сразу за листом Ïàíåëü óïðàâëåíèÿ и получает имя, соответствующее заданному диапазону, например 01.01.01 14.00– 02.01.01 09.00. Единственным нюансом этой функции является наличие в ней вызова процедуры GoNext (в конце листинга, выделена полужирным шрифтом). Это не стандартная процедура VBA, а вспомогательная. Она предназначена для управления выделенной ячейкой. Текст этой процедуры приведен ниже в листинге 9.14. ËÈÑÒÈÍÃ 9.14

Sub GoNext(sSelector As String) Dim Temp, Address, FirstPart, SecondPart, Scnd, Frst As String Dim SecondPartLen, FirstPartLen As Integer Address = ActiveCell.Address Temp = Right(Address, Len(Address) - 1) SecondPartLen = Len(Address) - InStr(Temp, "$") FirstPartLen = Len(Address) - SecondPartLen FirstPart = Left(Address, FirstPartLen) SecondPart = Right(Address, SecondPartLen) Scnd = "$" & LTrim(Str(Val( _ Right(SecondPart, SecondPartLen - 1)) + 1)) If Right(FirstPart, 1) < "Z" Then Frst = Left(FirstPart, FirstPartLen - 1) & _ Chr(Asc(Right(FirstPart, 1)) + 1) Else If FirstPartLen = 2 Then Frst = "$AA" Else Frst = "$" & Chr(Asc(Mid(FirstPart, 2, 1)) + 1) & "A" End If End If Select Case sSelector Case sRow Range(FirstPart & Scnd).Select Case sCol Range(Frst & SecondPart).Select Case sHome Range("$A" & SecondPart).Select Case sStart Range("$A$1").Select End Select End Sub Несмотря на довольно сложные вычисления, назначение процедуры простое — в зависимости от поступившего на вход селектора переместить маркер текущей ячейки на одну строку вниз (sRow), на один столбец вправо (sCol), в начало текущей строки (sHome) или в левую верхнюю ячейку рабочего листа (sStart). Определение возможных значений селектора приведено в листинге

250 Глава 9. Программирование в Outlook: документооборот и электронная почта 9.3. Эта процедура будет часто использоваться в других процедурах и функциях приложения при генерации отчета о зарегистрированных сообщениях. Поместите все фрагменты кода, приведенные в листингах 9.12–9.14 в модуль misc, сохраните проект и выполните тестовый “прогон приложения”. Для ускорения работы приложения на вопрос, нужно ли выполнять синхронизацию, можно ответить отрицательно. Если программный код был набран правильно, во время работы приложения на экране дважды появится диалоговое окно, предлагающее первый раз ввести дату начала, а второй — окончания периода, за который необходимо зарегистрировать почту, подобное тому, которое приведено на рис. 9.16. После ввода даты будет создан новый лист, имя которого должно состоять из дат и времени начала и окончания указанного периода. Убедившись, что приложение отработало правильно (в строке состояния появилось сообщение “Макрос выполнен успешно”), удалите только что созданный лист и перейдем к следующему этапу.

Рис. 9.16. Это окно будет “терзать” пользователя до тех пор, пока он не введет корректную дату

Êëþ÷åâàÿ ïðîöåäóðà ïðèëîæåíèÿ — âûáîðêà âõîäÿùèõ è èñõîäÿùèõ ïèñåì Теперь у нас есть все необходимое для разработки основной процедуры приложения, которая выполняет выборку входящих и исходящих писем и выводит найденную информацию на созданный для этой цели рабочий лист. Согласно листингу 9.5, эта процедура называется ProcessFolder. Как показано в листинге, она вызывается в главной программе два раза — на этапах 6 и 9. При первом вызове в качестве параметра ей передается стандартное название папки, которое используется всеми учетными записями для папки входящих сообщений, а при втором — стандартное название папки исходящих сообщений. Эти названия хранятся в константах sInbox и sSentItems, соответственно (см. листинг 9.3). Итак, откройте модуль main, найдите в нем заготовку процедуры ProcessFolder и поместите между операторами Sub и End Sub это йпроцедуры программный код согласно листингу 9.15. ËÈÑÒÈÍà 9.15

Sub ProcessFolder(FolderType As String) For Each Account In objOutlook.Session.SyncObjects Set CurFolder = FolderList(Account.Name).Folders(FolderType) Application.StatusBar = "Учетная запись " & Account & _ ", папка " & CurFolder

Создание приложения Registrator 251 For Each FolderItem In CurFolder.Items If FolderItem.MessageClass = "IPM.Note" Then Set CurMsg = FolderItem With CurMsg Selector = DateDiff("s", .ReceivedTime, DateEnd) If (Selector > 0) And (Selector < Interval) Then If FolderType = sInbox Then ActiveCell = .SenderName If .SentOnBehalfOfName .SenderName Then ActiveCell = ActiveCell & " (" & _ .SentOnBehalfOfName & ")" End If Else ActiveCell = .To If .CC "" Then ActiveCell = ActiveCell & _ ", копия: " & .CC If .BCC "" Then ActiveCell = ActiveCell & _ ", скрытая копия: " & .BCC End If ActiveCell.Font.Size = 10 - Len(ActiveCell) \ _ iGoodAttachWidth ActiveCell.WrapText = True If Len(ActiveCell) > 50 Then _ ActiveCell.VerticalAlignment = xlTop GoNext (sCol) ActiveCell = .Size: GoNext (sCol) If .Attachments.Count > 0 Then For Each Attach In .Attachments ActiveCell = ActiveCell & Attach.Filename & " " Next Attach ActiveCell = Left(ActiveCell, Len(ActiveCell) - 1) ActiveCell.WrapText = True ActiveCell.Font.Size = "6" Else ActiveCell = "нет" End If If Len(ActiveCell) > MaxAttachmentsLen Then MaxAttachmentsLen = Len(ActiveCell) End If GoNext (sCol) ActiveCell = .ReceivedTime ActiveCell.NumberFormat = "dd/mm/yyyy hh:mm" GoNext (sCol) ActiveCell = .Subject: GoNext (sCol) ActiveCell = Account.Name: GoNext (sRow) GoNext (sHome) End If End With '*** CurMsg End If Next FolderItem Next Account If FolderType = sInbox Then GoNext (sHome): GoNext (sRow)

252 Глава 9. Программирование в Outlook: документооборот и электронная почта End If End Sub Данная процедура решает основную задачу приложения — выбирает из локальных папок Outlook 2000 сведения о сообщениях, отправленных и полученных в течении заданного периода, и выводит эти сведения в виде таблицы Excel. Сама по себе процедура довольно проста, поэтому не будем детально ее разбирать, а ограничимся лишь кратким перечислением некоторых ее особенностей, которые, как говорится, “не лежат на поверхности”. • В основном цикле процедуры используется уже известный метод перебора учетных записей, основанный на последовательном получении ссылок на объекты коллекции SyncObjects текущего сеанса сервера автоматизации objOutlook. С помощью полученной ссылки на очередной объект синхронизации, представляющей собой ничто иное как учетную запись соответствующего пользователя, а также типа синхронизируемых при данном прогоне процедуры папок (задается в качестве параметра при вызове процедуры) инициализируется ссылка на объект CurFolder, представляющий папку Outlook 2000 с заданным названием, которая находится в списке папок заданной учетной записи. • Далее выполняется вложенный цикл, в котором перебираются все объекты коллекции Items заданной папки. В этом цикле может показаться странным использование промежуточного объекта FolderItem класса Object. Действительно, можно было бы в качестве переменной цикла использовать объект CurMsg, который относится к нужному в данном случае классу MailItem. Однако если попробовать выполнить такую замену, закомментировав строки, как показано на рис. 9.17, а также соответствующий оператор Next в конце процедуры, работа приложения может завершиться аварийно с выдачей сообщения об ошибке (рис. 9.17).

Рис. 9.17. Замена класса переменной цикла может привести к аварийному завершению работы приложения

Создание приложения Registrator 253 •



Если при возникновении такой ситуации щелкнуть на кнопке Debug окна сообщения и подвести указатель к переменной CurMsg, то в строке всплывающей подсказки, которая появится спустя несколько секунд, можно увидеть, что переменная CurMsg имеет значение Nothing (рис. 9.18), т.е. что она не была инициализирована. Дело в том, что в папке отправленных сообщений могут храниться не только обычные сообщения, который в Outlook 2000 представляются объектами класса MailItem, но и уведомления о получении сообщений, которым в объектной модели Outlook соответствует класс ReportItem. Поэтому, естественно, система не сможет, получив из коллекции Items объекта CurFolder ссылку на экземпляр класса ReportItem и присвоить это значение объектной переменной класса MailItem, которая в рассматриваемой процедуре представлена переменной CurMsg.

Рис. 9.18. Система не смогла создать экземпляр объекта класса MailItem •







Для того чтобы обойти это ограничение и используется переменная FolderItem класса Object. Так как класс Object является универсальным, с инициализацией этой переменной не возникает никаких проблем. Затем лишь остается проверить значение свойства MessageClass, которое имеется у всех объектов, представляющих элементы Outlook. У объектов, представляющих сообщения электронной почты, это свойство равно IPM.Note, а у объектов извещений — Report.IPM.Note.IPNRN. Простая проверка позволяет убедиться в том, что ссылку, которая хранится в переменной FolderItem можно присвоить объекту CurMsg. Если же среди элементов коллекции будет обнаружен объект класса ReportItem, его обработка выполняться не будет, а цикл перейдет к следующему элементу. Затем начинается обработка сообщения. Здесь снова используется функция DateDiff, с помощью которой определяется, попадает ли очередное сообщение в заданный пользователем интервал.

254 Глава 9. Программирование в Outlook: документооборот и электронная почта •







Если сообщение соответствует этому условию, процедура получает значения его свойств, выводя их в столбцах и строках листа Excel (обратите внимание на использование функции GoNext). В том случае, когда сообщение содержит вложения (т.е. свойство CurMsg.Attachments.Count имеет ненулевое значение), выполняется последовательный перебор всех элементов коллекции CurMsg.Attachments. Так как количество вложений и названия файлов могут иметь разную длину, процедура пытается отформатировать текущую ячейку таким образом, чтобы в ней поместилась вся соответствующая информация и чтобы ячейка при этом была достаточно компактной. При выводе даты получения или отправки сообщения применяется формат, обеспечивающий единообразное представление значения времени для последующей сортировки сообщений. Наконец, так как данная процедура универсальна и применяется для выборки как полученных, так и отправленных сообщений, в ней имеются операторы, которые немного изменяют ее алгоритм в соответствии с тем, какой тип папки обрабатывается, с целью упрощения последующего форматирования и улучшения внешнего вида отчета.

Рис. 9.19. Отчет о сообщениях, полученных и отправленных в период Рождественских каникул 2001 года (значительная часть отчета выходит за пределы экрана) Подготовив текст процедуры, сохраните проект и попробуйте выполнить тестовый “прогон приложения”. Если не было допущено ошибок, после успешного завершения работы макроса появится результат, подобный приведенному на рис. 9.19. На этом создание основной части приложения можно считать завершенным. Удалите только что созданный лист и сохраните проект.

Äîâîäêà ïðèëîæåíèÿ Registration äî ïðîôåññèîíàëüíîãî óðîâíÿ Конечно же, хотя в настоящее время уже можно получить полноценный отчет, работать с ним все же сложно, так как он не отформатирован. Нам осталось разработать лишь несколько вспомо-

Создание приложения Registrator 255 гательных процедур, которые облегчат восприятие отчета и обеспечат его профессиональный вид. Здесь возникает две проблемы — во-первых, количество строк в отчете, как по входящим сообщениям, так и по исходящим меняется от нуля до… очень больших значений. Следовательно, эти процедуры должны корректно определить, имеются ли входящие или исходящие сообщения вообще и, если это так, какой диапазон они занимают. Вторая проблема состоит в том, что отдельные элементы сообщений, такие как тема, перечень вложений или получателей копий письма, могут иметь очень большую длину, что нужно учесть при форматировании. Ниже приведены исходные тексты оставшихся процедур, которые позволяют решить указанные выше проблемы и получить полнофункциональное приложение, практически профессионального вида. Так как эти процедуры носят исключительно вспомогательный характер, изучив их исходный текст, можно привести хорошо известное высказывание: “О вкусах не спорят”. Данные процедуры приводятся только для справки, чтобы облегчить изучение излагаемого материала. Так как разработанное приложение имеет ярко выраженную модульную структуру можно, при желании, заменить эти процедуры другими. В листинге 9.16 приведены недостающие процедуры модуля main, обеспечивающие вычисление диапазонов ячеек, в которых содержатся сведения о зарегистрированных входящих и исходящих сообщениях. ËÈÑÒÈÍÃ 9.16

Sub CalculateInputRange() OutputDataRange = ActiveCell.Address If OutputDataRange "$" & sFirstCol & "$4" Then LastInputRow = LTrim(Str(Val(Mid(OutputDataRange, 4, _ Len(OutputDataRange) - 3)) - 2)) InputDataRange = "$" & sFirstCol & "$3:$" & sLastCol & _ "$" & LastInputRow Else '*** Нет ни одного входящего сообщения LastInputRow = "3" GoNext (misc.sStart): GoNext (sRow): GoNext (sRow) ActiveCell = "За указанный период сообщений не было" InputDataRange = ActiveCell.Address & ":" GoNext (sRow) GoNext (sRow) End If End Sub Sub CalculateOutputRange() OutputDataRange = ActiveCell.Address LastInputRow = "$" & sFirstCol & "$" & _ LTrim(Str(Val(LastInputRow) + 4)) If OutputDataRange LastInputRow Then OutputDataRange = LastInputRow & ":$" & sLastCol & "$" & _ LTrim(Str(Val(Mid(OutputDataRange, 4, _ Len(OutputDataRange) - 3)) - 1)) Else '*** Нет ни одного исходящего сообщения OutputDataRange = ActiveCell.Address & ":" ActiveCell = "За указанный период сообщений не было" End If End Sub В листинге 9.17 приведены процедуры модуля misc, обеспечивающие создание заголовков отчетов, а также окончательное форматирование сформированных отчетов.

256 Глава 9. Программирование в Outlook: документооборот и электронная почта

Рис. 9.20. Отформатированный отчет воспринимается гораздо лучше ËÈÑÒÈÍÃ 9.17

Sub MakeReportHeader(sFolderType As String, _ ByVal sHeaderText As String) Dim sRange, sHeaderRange As String ActiveCell = sHeaderText sRange = ActiveCell.Address sRange = sRange & sSemicolon & sDollar & sLastCol & _ sDollar & Mid(sRange, 4, Len(sRange) - 3) sHeaderRange = sRange Range(sRange).Select With Selection .HorizontalAlignment = xlCenter .VerticalAlignment = xlBottom .WrapText = False .Orientation = 0 .AddIndent = False .ShrinkToFit = False .MergeCells = False End With Selection.Merge Selection.Font.Bold = True MakeBorder (iTopHeader) GoNext (sHome): GoNext (sRow) If sFolderType = sInbox Then

Создание приложения Registrator 257 ActiveCell = "От кого" Else ActiveCell = "Кому" End If: sRange = ActiveCell.Address: GoNext (sCol) ActiveCell = "Размер": GoNext (sCol) ActiveCell = "Вложения": GoNext (sCol) ActiveCell = "Дата и время": GoNext (sCol) ActiveCell = "Тема": GoNext (sCol) ActiveCell = "Учетная запись": GoNext (sCol) sRange = sRange & sSemicolon & sDollar & sLastCol & _ sDollar & Mid(sRange, 4, Len(sRange) - 3) Range(sRange).Select MakeGrid (IsOneRow(sRange)) MakeBorder (iBottomHeader) Selection.Font.Size = 8 Selection.Font.Bold = True Selection.HorizontalAlignment = xlCenter GoNext (sHome) GoNext (sRow) End Sub Sub FormatReport(Selector As String) If Right(Selector, 1) = sSemicolon Then Selector = Selector & sDollar & sLastCol & sDollar & _ Mid(Selector, 4, Len(Selector) - 4) Range(Selector).Select Selection.Font.Bold = True Selection.Font.Italic = True MakeBorder Else Range(Selector).Select Selection.RowHeight = iGoodHeight MakeGrid (IsOneRow(Selector)) MakeBorder Selection.Columns.AutoFit Selection.Sort Key1:=Range(sTimeKey), Order1:=xlAscending, _ Header:=xlNo, OrderCustom:=1, _ MatchCase:=False,Orientation:=xlTopToBottom Range(sAddressCol).Select Selection.ColumnWidth = iGoodWidth Range(sSubjectCol).Select Selection.ColumnWidth = iGoodWidth Range(sAttachmentsCol).Select If MaxAttachmentsLen > iGoodAttachWidth Then _ MaxAttachmentsLen = iGoodAttachWidth Selection.ColumnWidth = MaxAttachmentsLen / 2 GoNext (sStart) End If End Sub Sub MakeGrid(JustOneRow As Boolean) Selection.Borders(xlEdgeLeft).LineStyle = xlContinuous Selection.Borders(xlEdgeTop).LineStyle = xlContinuous Selection.Borders(xlEdgeBottom).LineStyle = xlContinuous Selection.Borders(xlEdgeRight).LineStyle = xlContinuous

258 Глава 9. Программирование в Outlook: документооборот и электронная почта Selection.Borders(xlInsideVertical).LineStyle = xlDouble If Not JustOneRow Then _ Selection.Borders(xlInsideHorizontal).LineStyle = _ xlContinuous End Sub Sub MakeBorder(Optional Control As Integer = 0) With Selection.Borders(xlEdgeLeft) .LineStyle = xlContinuous .Weight = xlMedium End With With Selection.Borders(xlEdgeTop) .LineStyle = xlContinuous If Control = iBottomHeader Then .Weight = xlThin Else .Weight = xlMedium End If End With With Selection.Borders(xlEdgeBottom) .LineStyle = xlContinuous If Control = iTopHeader Then .Weight = xlThin Else .Weight = xlMedium End If End With With Selection.Borders(xlEdgeRight) .LineStyle = xlContinuous .Weight = xlMedium End With End Sub Function IsOneRow(ByVal sRange As String) As Boolean Dim SemicolonPosition As Integer IsOneRow = False SemicolonPosition = InStr(sRange, ":") If Len(Left(sRange, SemicolonPosition - 1)) = _ Len(Right(sRange, Len(sRange) - SemicolonPosition)) Then IsOneRow = StrComp(Mid(sRange, 4, SemicolonPosition - 4), _ Mid(sRange, SemicolonPosition + 4, _ Len(sRange) - SemicolonPosition - 3)) = 0 End If End Function Sub FormatHeaderCell() With Selection .Borders(xlEdgeLeft).LineStyle = xlContinuous .Borders(xlEdgeTop).LineStyle = xlContinuous .Borders(xlEdgeBottom).LineStyle = xlContinuous .Borders(xlEdgeRight).LineStyle = xlContinuous .Font.Size = 8 .Font.Bold = True .HorizontalAlignment = xlCenter End With

Создание приложения Registrator 259 End Sub Создав указанные процедуры, сохраните проект и запустите приложение. Теперь получаемые с помощью приложения Registrator результаты будут соответствовать тем, которые представлены на рис. 9.20 и 9.21.

Рис. 9.21. Ситуация с отсутствующими сообщениями обрабатывается корректно

Äîâîäêà è ñîâåðøåíñòâîâàíèå ïðèëîæåíèÿ Итак, специалисты ООО “Универсал” с честью справились с возложенной на них задачей — в результате долгого и кропотливого труда было создано надежное и устойчиво работающее приложение, обеспечивающее автоматическую регистрацию всех входящих и исходящих сообщений. Естественно, руководству очень скоро оказалось этого мало: “А можно ли выводить отчет по определенной учетной записи? А по всем учетным записям? А если нужно получить сведения об объемах пересылаемой почты по месяцам или по кварталам? А нельзя ли создать отчет в цвете для распечатки на цветном принтере? Как насчет идеи сохранения всей регистрационной информации в базе данных Access 2000?…” Да, действительно, данное приложение дает возможность использовать его в качестве исходной платформы, совершенствуя и развивая его в нужном направлении. Остается лишь пожелать успехов в этом нелегкой, но интересной работе.

Приложение VBA, êàê ÿçûê ïðîãðàììèðîâàíèÿ: äàííûå, ñèíòàêñèñ è ôóíêöèè Как язык программирования, VBA во всем похож на Visual Basic (об этом говорит уже само название VBA — Visual Basic для приложений). Не следует путать VBA c Visual Basic, поскольку это физически разные вещи — Visual Basic представляет собой самостоятельный пакет для разработки программного обеспечения, не имеющий прямого отношения к пакету MS Office. Программы на языке Visual Basic — это самостоятельные исполняемые файлы, такие же, как MS Word или MS Excel, в то время как VBA-программы “живут” только в рабочей среде документов Office, поскольку VBA — это одно из встроенных инструментальных средств приложений Office. Однако, с точки зрения программиста — в синтаксическом, и во многих других отношениях — Visual Basic и VBA (Visual Basic для приложений), это одно и то же. Более того, у VB и VBA есть еще третий “брат-близнец”: язык описания Web-сценариев VBScript, который также совместим на уровне синтаксиса с VBA и VB. В этом заключается великая сила и огромное преимущество технологии Visual Basic, которая использует один универсальный язык для решения столь различных задач. И в этом же заключается главная причина, по которой любому человеку, имеющему дело с компьютерами, стоит хоть немного владеть синтаксисом Visual Basic — никогда не знаешь, где он сможет тебе пригодиться. Язык программирования Visual Basic позволяет заниматься как офисным программированием на VBA, так и созданием Windows-приложений при помощи пакета Visual Basic, а еще можно создавать Web-сценарии на языке VBScript. “Три в одном” — это ли не причина для того, чтобы хоть немного освоить азы языка программирования Visual Basic (особенно, учитывая тот факт, что многие приложения сторонних производителей в наше время снабжаются встроенными языками, очень похожими на Visual Basic)? Как знать, не придется ли нам всем в недалеком будущем программировать новый холодильник или стиральную машину именно на языке Visual Basic?

Äàííûå VBA: òèïû äàííûõ, ïåðåìåííûå è êîíñòàíòû Изучение любого языка программирования начинают с данных, которыми оперируют программы на этом языке. В самом деле, невозможно понять, как выполнить ту или иную операцию над каким-нибудь объектом, не понимая, что этот объект собой представляет. В случае Visual Basic дело обстоит и проще, и в то же время сложнее. Никакой “серьезный” язык программирования не позволит использовать переменные, которые не были предварительно объявлены. В Visual Basic разрешено использовать данные без явного или неявного указания их типа. Можно вообще не задумываться о данных, придумывая переменные “на ходу” и тут же используя их в своем коде, как заблагорассудится, объявляя переменную без указания ее типа и используя ее для хранения данных практически любого типа. Однако за все надо платить, и платой за такую свободу будет низкая эффективность программного кода, а также большое количество ошибок. Как показывает практика, только небольшие и несложные программы можно создавать подобным способом. Но Visual Basic гибок и универсален, и если вы хотите работать серьезно, и создавать скольконибудь сложные программы, то у вас есть возможность обращаться с данными так, как это делают профессиональные программисты.

Данные VBA: типы данных, переменные и константы 263

Ïåðåìåííûå è êîíñòàíòû Для хранения данных используют переменные и константы. Разница между ними заключается в том, что переменная может менять свое значение в процессе выполнения программы, а константа — это просто какое-то значение, которому для удобства обращения с ним присвоено имя.

Êîíñòàíòû Константы применяют в случаях, когда требуется много раз использовать в программе одно и то же значение. Тогда для того, чтобы изменить это значение, достаточно будет изменить только текст объявления константы. Кроме этого, константы делают текст программы более “прозрачным” и легким для понимания. Например, при вызове многих функций используются предопределенные константы Visual Basic, например: If MsgBox("Создать письмо?", vbYesNo, "Отрицательное сальдо") = _ vbYes ... ... Здесь константы vbYes и vbYesNo — это просто числа, запоминать которые нет нужды, поскольку имена констант запомнить гораздо легче. Константа vbYesNo означает, что в окне сообщения должны присутствовать кнопки Yes (Äà) и No (Íåò), а константа vbYes означает, что пользователь выбрал кнопку Yes(Äà). Существует огромное число таких предопределенных констант, при помощи которых задают параметры для функций, свойств и методов объектов, а также используют во многих других случаях. Îáúÿâëåíèå êîíñòàíò Программист может сам определить константы для собственных нужд. Например, можно определить константу для процентной ставки, используемой в вычислениях: Public Const Stavka1 As Single = 0.16667 Здесь Stavka1 — это имя константы: далее в программе можно использовать это имя везде, где требуется значение 0.16667. Зарезервированное слово Public означает, что константа будет “видна” во всех модулях и во всех процедурах. Такие данные должны объявляться на уровне модуля, то есть вне текста какой-либо процедуры. Тип числового значения здесь определен, как Single (о типах см. далее). Если вместо Public использовать слово Private, то константу будут “понимать” только те процедуры, которые находятся только в этом же модуле. Далее можно объявить еще одну константу, используя при этом уже объявленное имя Stavka1: Public Const Stavka2 As Single = Stavka1 + 0.007 И опять надо будет использовать имя Stavka2 везде, где требуется значение 0.16667 + 0.007. В результате можно будет, изменив всего одну строку кода, поменять значения всех ставок, которые участвуют в вычислениях. Константы, которые используются локально, то есть только в той процедуре, где они объявлены, задаются без слов Public или Private, их объявление помещается внутри текста процедуры. Например, можно определить константами текст сообщения об ошибке и максимальное число ошибок: Const ErrorMessage1 As String = "Произошла ошибка 1" ... Const MaxErrors As Integer = 100

264 Приложение. VBA, как язык программирования: данные, синтаксис и функции Ïåðåìåííûå Переменные, в противоположность константам, могут менять свое значение в процессе выполнения программы. Но, как и к константам, обращение к переменным производится по их именам. Переменная может принадлежать к одному из простых типов или же к типу, определенному пользователем, как комбинация простых типов. Кроме этого, переменная может быть объявлена, как массив. Îáúÿâëåíèå ïåðåìåííûõ Рассмотрим следующие примеры объявления переменных: Dim WordRunning As Boolean Логическая (булева) переменная WordRunning может принимать только значения True (Истина) или False (Ложь). Dim WordCount, WordPointer As Integer Целочисленным переменным WordCount и WordPointer отныне разрешено присваивать в качестве значений только целые числа. А вот хорошо знакомые примеры объявления объектных переменных: Dim MyParagraph As Paragraph Dim MyDocument As Document Если тип переменной явно не указан, по умолчанию будет принят тип Variant (о типе Variant и вообще о типах см. далее). Èíèöèàëèçàöèÿ ïåðåìåííûõ Автоматически, в момент запуска программы, переменные численных типов инициализируются значением 0, а переменные строковых типов — значением “пустая строка”. Переменные типа Variant инициализируются значением Empty, а переменные объектных типов — значением Nothing. Объявление переменных с областью видимости на уровне модуля необходимо помещать в начало модуля, вне текста какой-либо процедуры. Объявление переменных внутри процедуры следует помещать в начало процедуры (это повышает “прозрачность” текста и облегчает его понимание). Везде, где это возможно, желательно использовать локальные переменные на уровне процедуры — неумеренное использование переменных, объявленных на уровне модуля, усложняет текст и затрудняет его понимание. Çà÷åì îáúÿâëÿòü ïåðåìåííûå? Вообще говоря, переменные можно и не объявлять — Visual Basic может, при желании, допускать такую свободу в обращении со своими данными. Можно просто использовать любое имя в левой части оператора присваивания: SvodCount = 2 И затем, внутри цикла For, использовать и модифицировать значение получившейся переменной SvodCount: For A = 1 To PriceA.Range("Наименование").Rows.Count ... SvodCount = SvodCount + 1 ... Next A

Данные VBA: типы данных, переменные и константы 265 И все будет работать точно так же, как и если бы предварительно была объявлена переменная SvodCount. Однако, если в результате опечатки или забывчивости, имя используемой в цикле переменной будет отличаться от имени переменной, которой предварительно присваивалось значение, то это окажутся две разные переменные, причем вторая будет автоматически инициализирована значением 0, поскольку используется в контексте, предполагающем целочисленные значения. Например: SvodCount = 2 For A = 1 To PriceA.Range("Наименование").Rows.Count ... SvodCaunt = SvodCount + 1 ... Next A Предположим, в результате забывчивости или просто из-за опечатки в цикле было введено имя SvodCaunt вместо SvodCount. Никто ничего не заметит, и программа будет работать. Но работать она будет неправильно, поскольку переменная SvodCaunt будет автоматически создана помимо переменной SvodCount, и начальным значением ее будет число 0, а вовсе не 2. Значение же переменной SvodCount будет оставаться неизменным внутри цикла, хотя предполагалось, что оно будет увеличиваться на 1 в каждом проходе. Когда (и если!) будет обнаружено, что программа работает неправильно, придется искать, в чем же ошибка. Если в программе используется 2-3 переменные, которые упоминаются 2-3 десятка раз, то даже тогда придется немало потрудиться, чтобы найти подобную ошибку. Если же переменных несколько десятков и упоминаются они сотни раз, то, скорее всего, ошибку нельзя будет найти никогда — программу придется переписывать заново. Но это еще не самый худший случай, — возможно, сам факт наличия ошибки не будет обнаружен, и придется пользоваться неправильно работающей программой, пока на ошибку не укажет сама жизнь. Äèðåêòèâà Option Explicit Для того чтобы Visual Basic перестал “понимать” необъявленные переменные, используют директиву Option Explicit. Эта директива помещается в начале исходного текста и любые переменные, упоминаемые после нее, обязательно должны быть предварительно объявлены. Если рассмотреть предыдущий пример с учетом директивы Option Explicit, то картина разительно переменится. Например: Option Explicit ... Dim SvodCount As Integer Dim A As Integer ... SvodCount = 2 ... For A = 1 To PriceA.Range("Наименование").Rows.Count ... SvodCaunt = SvodCount + 1 ... Next A При попытке запуска программы компилятор Visual Basic обнаружит ошибку и, вместо того, чтобы запустить программу, выдаст сообщение: “Ошибка компиляции: переменная не определена”, выделив при этом в тексте ошибочное имя SvodCaunt (рис. П.1).

266 Приложение. VBA, как язык программирования: данные, синтаксис и функции

Рис. П.1. Компилятор обнаружил необъявленную переменную Опечатка обнаружена сразу же, а поиск ее не стоил программисту никакого труда. Если создается простенький макрос “на скорую руку”, то можно не объявлять переменные и не использовать директиву Option Explicit. Но как только программа станет немного сложнее, то придется использовать директиву Option Explicit и заранее объявлять каждую переменную, поскольку в противном случае вряд ли удастся отладить программу, а если и удастся, то никогда нельзя быть уверенным в том, что она работает правильно. Îáëàñòü âèäèìîñòè è âðåìÿ æèçíè ïåðåìåííûõ Переменные и константы, кроме всего прочего, характеризуются областью видимости — областью, где они “видны”, то есть, доступны для программы. За пределами своей области видимости переменная или константа не может быть использована программой. Область видимости переменной или константы определяется двумя обстоятельствами: где она объявлена, и какое дополнительное ключевое слово было при этом использовано. Предположим, в исходном тексте (это может быть модуль, форма, рабочая книга или документ), объявлены переменные A, B и C: Public A As Integer Dim B As Integer ... Sub MySub() Dim C As Integer ... End Sub Глобальная переменная A будет доступна во всех процедурах всех модулей, относящихся к данной рабочей книге или документу. Переменная B доступна только в пределах модуля, то есть в этом же окне исходного текста. Если где-то в другом модуле объявлена глобальная переменная с таким же именем (Public B), то в процедурах этого модуля будет фактически использована эта переменная B, а в остальных местах под B будет иметься в виду глобальная, то есть Public-переменная B. Переменная C определена только внутри процедуры MySub. Если переменная с таким же именем объявлена на уровне модуля, то внутри процедуры MySub будет все равно использоваться “своя”, локальная переменная C. Существует еще область видимости Private — такие переменные доступны в пределах модуля, но недоступны в других модулях. Это область видимости по умолчанию, и ее можно явно не указывать — объявление Dim на уровне модуля (то есть вне текста какой-либо процедуры) подразумевает Private. Кроме области видимости, локальные и глобальные переменные имеют еще одно важное отличие. Глобальные переменные существуют и сохраняют свое значение все время, пока выполняется программа. Локальные переменные, объявленные внутри процедуры, существуют только во время выполнения процедуры. Например:

Данные VBA: типы данных, переменные и константы 267 Public A As Integer Dim B As Integer ... Sub MySub() Dim C As Integer A = 10 B = 20 C = 100 ... End Sub После того, как процедура MySub завершит свою работу, переменная C “исчезнет” вместе со своим значением, и снова она возникнет уже со значением 0 в момент следующего вызова процедуры MySub. А переменные A и B сохранят присвоенные им значения. Причем значение A сможет прочитать процедура любого модуля, а значение B будет доступно только в пределах текущего модуля. Благодаря глобальным переменным процедуры могут передавать друг другу, или, скорее, оставлять друг для друга сохраненные данные. Существует возможность сохранять значения локальных переменных. Если объявить переменную внутри процедуры с использованием зарезервированного слова Static, то она будет продолжать существовать и после завершения процедуры, сохраняя свое значение. Например: Sub MySub() Static C As Integer ... End Sub

Ìàññèâû Массивом называют совокупность переменных одного типа, доступ к которым осуществляется по их общему имени (имени массива) и номеру переменной в массиве (индексу). Применение массивов упрощает операции с массивами данных. Например: Const MaxWords = 3000 Const WordsInDictionary = 200 Dim Words(0 To MaxWords) As String Dim WordFrq(0 To MaxWords) As Integer Если нижняя граница индекса не указана, то по умолчанию она принимается равной 0. При помощи директивы Option Base значение нижней границы по умолчанию можно изменить, например Option Base 1 задаст отсчет нижней границы по умолчанию с 1. Таким образом, массив Words, например, можно было бы определить, как Dim Words(3000) As String Если бы требовалось использовать две размерности в массиве, достаточно было бы указать два набора границ: Dim Words(0 To 30, 0 To 100) As String Äèíàìè÷åñêèå ìàññèâû В Visual Basic поддерживаются динамические массивы, то есть массивы, размерность которых меняется при выполнении программы. Например, можно было бы не указывать размерность массива Words при его объявлении:

268 Приложение. VBA, как язык программирования: данные, синтаксис и функции Dim Words() As String Но затем, перед первым обращением к элементам такого массива, необходимо будет задать его размерность при помощи оператора ReDim: ReDim Words(0 To 3000) При необходимости изменить размерность, ее снова можно переопределить: ReDim Words(0 To 4000) Если новая размерность больше старой, в массиве просто появятся дополнительные пустые элементы, в данном случае строки. Если новая размерность меньше старой размерности, то значения части элементов будет утеряны. При помощи функций LBound и UBound можно получить текущие значения нижней и верхней границ динамического массива соответственно. Операторы MsgBox LBound(Words) MsgBox UBound(Words) выведут на экран значения 0 и 4000.

Îáúåêòíûå ïåðåìåííûå Объектную переменную можно объявить, как абстрактный объект (тип Object), или указать для нее определенный объектный тип. Переменная типа Object (ссылка на объект) может указывать на объект любого типа. Переменная, объявленная, как ссылка на объект определенного типа, может указывать на объект только этого типа. Присваивание ей в качестве значения объекта другого типа вызовет ошибку. Присваивание объектного значения (точнее, ссылки на объект) производится при помощи зарезервированного слова Set: Dim Dim ... Set Set

MyParagraph As Paragraph MyDocument As Document MyDocument = Documents.Add MyParagraph = MyDocument.Paragraphs.Add

С таким же успехом можно объявить обе переменные, как объект, и присвоить им в качестве значения объекты типа “документ” и “абзац”, … Dim Dim ... Set Set

MyParagraph As Object MyDocument As Object MyDocument = Documents.Add MyParagraph = MyDocument.Paragraphs.Add

… или наоборот: Set MyParagraph = Documents.Add Set MyDocument = MyDocument.Paragraphs.Add В любом случае при этом произойдет так называемое “позднее связывание” и фактический тип объектов MyDocument и MyParagraph определится уже в процессе выполнения программы. Такой способ работы с объектами связан с определенными сложностями, и использовать его следует только в тех случаях, когда тип возвращаемого объекта действительно заранее неизвестен.

Данные VBA: типы данных, переменные и константы 269 Определить тип объекта, ссылка на который получена откуда-то извне (например, как возвращаемое значение какой-то функции), можно при помощи оператора TypeOf и операции Is: Dim MyObject As Object ... Set MyObject = MyFunction() ... If TypeOf MyObject Is Document Then MyObject.Paragrahps.Add ... End If If TypeOf MyObject Is Workbook Then MyObject.WorkSheets.Add ... End If

Òèïû äàííûõ Все данные, которыми оперирует VBA-код, относятся к определенному типу. Тип переменной можно не указывать при ее объявлении, но это не значит, что она не будет относиться ни к какому типу: просто неявным образом будет использован универсальный тип по умолчанию, именуемый Variant.

Çà÷åì íóæíî îáúÿâëÿòü òèï? Зачем же объявлять тип переменной, если этого можно не делать? Существует, по меньшей мере, две серьезных причины для явного указания типа данных, с которыми предполагается иметь дело. Первая причина заключается в том, что за свободу в обращении с типами приходится платить эффективностью. Если требуется переменная I для хранения целочисленных значений, то можно объявить ее с явным указанием целочисленного типа Integer: Dim I As Integer или же просто, как Dim I или, что то же самое, Dim I As Variant Как уже указывалось, если тип не указан, то по умолчанию используется тип Variant. Так можно делать, и программа будет нормально работать. Если таких переменных немного и обращение к ним происходит небольшое число раз, то никакой разницы программа “не почувствует”. Но разница все же есть. Переменная типа Variant занимает в несколько раз больше места в памяти компьютера, чем переменная типа Integer, а обращение к ней происходит во много раз медленнее. Поэтому, если используются большие массивы и циклы, то переменные типа Variant способны существенно замедлить работу программы. Вторая причина для явного указания типов всех переменных заключается во все тех же опечатках и ошибках, которые неизбежно случаются при работе с исходным текстом. Если тип переменной I, которая упоминалась выше, не указан явным образом, то можно присвоить ей любое значение, например строковое: I = “Строковое значение”

270 Приложение. VBA, как язык программирования: данные, синтаксис и функции В такой возможности кроется неиссякаемый источник ошибок. Например, выражение I = “1” + “1” никоим образом не эквивалентно выражению I = 1 + 1 В первом случае переменная I получит строковое значение “11”, а во втором — числовое значение 2. Если тип I не указан явным образом, то оба варианта одинаково правомерны. Но если I — это переменная типа Integer, то попытка присвоить ей строковое значение вызовет ошибку выполнения и программист обнаружит, что он что-то напутал. В противном случае поиски подобных ошибок могут стать бесконечными.

Ïðîñòûå òèïû äàííûõ Все основные (простые) типы данных, обычно используемые в языках программирования, поддерживаются и в VBA. Рассмотрим их. Boolean Может принимать только значения True (Истина) или False (Ложь). Переменные типа Boolean используются главным образом при организации циклов и ветвлений — обычно они играют роль флажков, сигнализирующих о состоянии программы. Например, можно сохранить в Boolean-переменной результат логической операции, скажем, результат сравнения двух чисел: Result As Boolean Dim N1 As Integer Dim N2 As Integer ... N1 = 10 N2 = 5 ... Result = N1 > N2 ... If Result=True Then MsgBox “Первое число больше второго!” End If Integer Этим переменным можно присваивать только целочисленные значения. Наименьшее значение: –32768 Наибольшее значение: +32767 Integer-переменные отличаются малым объемом занимаемой памяти и высокой скоростью обращения. Часто их используют в качестве счетчика цикла: Dim Counter As Integer ... For Counter = 1 to 1000 ... Next Counter Long Этим переменным также можно присваивать только целочисленные значения. От Integer этот тип отличается более широким диапазоном значений. Наименьшее значение: –2147483648

Данные VBA: типы данных, переменные и константы 271 Наибольшее значение: +2147483647 Long-переменные отличаются высокой скоростью обращения, и наилучшим образом подходят для задач целочисленной арифметики. Например: Dim MyDistance As Long Dim MyTotalTime As Long Dim MySpeed As Long ... MySpeed = 2521 MyTotalTime = 811 MyDistance = MySpeed * MyTotalTime  см. об еще одном способе объявления Long-переменных в раздел “Операции со строками” этой главы.

Single Этот тип используется для вычислений с плавающей точкой. Single-переменные обеспечивают точность до 6-го знака после десятичной точки. Наименьшее отрицательное значение: –3.402823E+38 Наибольшее отрицательное значение: –1.401298E–45 Наименьшее положительное значение: 1.401298E–45 Наибольшее положительное значение: 3.402823E+38 Например, с помощью переменных типа Single можно выполнять вычисления с дробями: Dim MyWeight As Single Dim MyPrice As Single Dim PriceToWeight As Single ... MyWeight = 3.1 MyPrice = 121.77 PriceToWeight = MyPrice / MyWeight Double Этот тип также используется для вычислений с плавающей точкой. Double-переменные обеспечивают точность до 14-го знака после десятичной точки. Наименьшее отрицательное значение: –1.79769313486232E+308 Наибольшее отрицательное значение: –4.94065645841247E–324 Наименьшее положительное значение: 4.94065645841247E–324 Наибольшее положительное значение: 1.79769313486232E+308 Тип Double используют, когда требуется обеспечить высокую точность вычислений с дробными числами. Например, вот как может выглядеть вычисление площади круга: Dim MyPI As Double Dim MyRadius As Double Dim MySquare As Double ... MyPI = 3.14159265358979 MyRadius = 9.79921235715865 MySquare = MyPI * MyRadius * 2 Currency Значения денежного типа Currency используют при финансовых расчетах различного рода. Необходимо помнить, что в денежной арифметике действуют несколько иные законы, чем в

272 Приложение. VBA, как язык программирования: данные, синтаксис и функции арифметике обычной. Если речь идет о денежных суммах, то корректность арифметических операций будет обеспечена только при использовании специального денежного типа Currency. Наименьшее значение: –922'337'203'685'477.5808 Наибольшее значение: +922'337'203'685'477.5807 В значениях типа Currency допускается использование не более чем четырех знаков после десятичной точки. Currency-переменные отличаются высокой скоростью вычислений, поскольку для внутреннего представления данных этого типа используются целочисленные форматы. Вот как, например, следует выполнять вычисления с денежными суммами в пересчете на валюту по курсу: Dim MySum As Currency Dim Kurs As Currency Dim MyUSDSum As Currency ... MySum = 123.45 Kurs = 6.77 MyUSDSum = MySum / Kurs Date Данные типа Date служат для операций со значениями даты-времени. Наименьшее значение: 1 января 100 года Наибольшее значение: 31 декабря 9999 года Значения даты и времени ограничиваются символами #...# или "...", например: MyDate = "04/02/1962" или MyDate = #17/04/1972# Значения даты и времени имеют внутреннее представление в виде числа с десятичной дробью, при этом целая часть числа соответствует дате, а дробная — времени. Это позволяет использовать операции сложения и вычитания в отношении дат и времени. Такие особенности, как разное количество дней в разных месяцах, а также особенности високосного года, учитываются автоматически. Вот пример вычисления со значениями даты — на экран выводится сообщение о количестве дней, прошедших со дня рождения: Dim BirthDay As Date Dim TodayDay As Date Dim DaysAge As Long BirthDay = "17/04/1972" TodayDay = #26/01/2001# DaysAge = TodayDay - BirthDay MsgBox DaysAge String Для выполнения действий с текстовыми строками используют переменные типа String. Максимальная длина строки: 2 миллиарда символов. Строковые значения необходимо заключать в кавычки: Dim MyString As String MyString = "Строковое значение"

Данные VBA: типы данных, переменные и константы 273 Специальным видом строки является строка фиксированной длины. Ее максимальная длина равняется 65536 символов (конкретная длина указывается при объявлении). Обращение со строками фиксированной длины отличается некоторыми особенностями. Например: Dim MyString1 As String Dim MyString2 As String *30 MyString1 = "Строковое значение1" MyString2 = "Строковое значение2" В результате переменная MyString1 содержит строку “Строковое значение1”, состоящую ровно из 19 символов, а в переменной MyString2 содержится строка длиной ровно 30 символов. При этом первые 19 символов — это “Строковое значение2”, а следующие 11 позиций заполнены пробелами. На операции со строками фиксированной длины накладывается ряд ограничений, поэтому их используют только в специальных случаях. Variant Переменная типа Variant способна принять значение любого из простых типов. Причем конкретный тип значения, которое принимает переменная, определяется в момент присваивания. Variant — тип по умолчанию для всех переменных, которые не были объявлены или же были объявлены без указания типа. Variant-переменная занимает больше места в памяти, чем переменная любого простого типа, чьи функции способна выполнять Variant-переменная. Применения этого типа следует по возможности избегать, поскольку обращение к данным типа Variant происходит медленнее, места в памяти они занимают больше, а программу с их использованием труднее отлаживать. Например: Dim MyFloat As Variant Dim MyInteger As Variant Dim MyText As Variant MyInteger = 19 MyFloat = 2.001 MyText = MyInteger * MyFloat & " попугаев" В результате в переменной MyText окажется строковое значение “38,019 попугаев”, причем строка “38,019” получилась неявным образом из числа 38,019, которое, в свою очередь, получено в результате операции умножения целого числа 19 и числа с плавающей точкой 2.001.

Òèïû äàííûõ, îïðåäåëÿåìûå ïîëüçîâàòåëåì Определяемые пользователем типы данных называют также записями или структурами. Такой тип представляет собой некую совокупность типов (простых или определенных ранее), объединенных общим именем. Если затем объявить переменную, принадлежащую к вновь определенному типу, то доступ к ее компонентам можно будет осуществлять по имени переменной и, отделенному точкой, имени компоненты, каким оно было задано при объявлении типа. В отличие от массивов такой механизм позволяет объединять в единую структуру переменные разных типов. Предположим, требуется создать специальный пользовательский тип для процедуры, которая будет обрабатывать личные данные сотрудников фирмы. В этом случае необходимо объявить тип (назовем его, например, Sotrudnik) на уровне модуля, то есть вне текста процедуры, которая его использует: Private Type Sotrudnik PersNum As Long Name As String

274 Приложение. VBA, как язык программирования: данные, синтаксис и функции BirthDay As Date Salary As Currency End Type После этого можно использовать в процедуре тип Sotrudnik точно так же, как и любой простой тип. Например, можно объявить две переменные этого типа: Dim MySotrudnik1 As Sotrudnik Dim MySotrudnik2 As Sotrudnik Для того чтобы присвоить значения компонентам переменной MySotrudnik1, необходимо через точку указывать имя соответствующей компоненты: MySotrudnik1.PersNum = 121 MySotrudnik1.Name = "Евгения" MySotrudnik1.BirthDay = "26/01/2000" MySotrudnik1.Salary = 7900.11 Далее можно обращаться с переменной MySotrudnik1, как с единым целым. Например, присвоить ее значение переменной такого же типа: MySotrudnik2 = MySotrudnik1 В результате выражение MySotrudnik2.BirthDay вернет значение даты "26/01/2000".

Ñèíòàêñè÷åñêèå êîíñòðóêöèè ÿçûêà VBA В простейших случаях, когда не происходит общения с пользователем или операционной системой, не выполняются сложные преобразования данных, программирование сводится к построению выражений, которые участвуют в операциях присваивания, и управлению ходом выполнения программы при помощи специальных операторов и функций, которые передают управление различным фрагментам кода в зависимости от выполнения тех или иных условий.

Âûðàæåíèÿ è îïåðàöèè Если переменная — это то, что может находиться в левой части оператора присваивания, то выражение — это то, что может находиться в правой части оператора присваивания. Под выражением в Visual Basic понимают другую переменную, функцию, свойство некоторого объекта или значение (числовое, строковое и т.д.), или результат разрешенных над соответствующими типами данных операций. Конечно, тип операндов и результата должен быть совместимым с типом переменной.

Àðèôìåòè÷åñêèå îïåðàöèè Арифметические операции выполняются над числовыми значениями. Их результатом также является числовое значение. В качестве операндов могут выступать переменные, константы и выражения. В табл. П.1 приведен набор арифметических операций, допустимых в VBA-программах: ÒÀÁËÈÖÀ Ï.1. ÀÐÈÔÌÅÒÈ×ÅÑÊÈÅ ÎÏÅÐÀÖÈÈ VBA

Операция

Описание

+ – / * ^

Сложение Вычитание или перемена знака Деление Умножение Возведение в степень

Синтаксические конструкции языка VBA 275 \ Mod

Целочисленное деление Деление по модулю (остаток от целочисленного деления)

Одноместные операции возведения в степень и перемены знака выполняются раньше всех остальных, операции *, /, \, Mod выполняются раньше + и –. Операции, имеющие одинаковый приоритет, выполняются слева направо. Последовательность выполнения операций можно явно задать при помощи скобок.

Ëîãè÷åñêèå îïåðàöèè Логические операции выполняются над логическими (булевыми) значениями. Кроме этого, логические операции могут выполняться над целыми числами — в этом случае речь будет идти о выполнении логической операции над соответствующими двоичными разрядами операндов. В табл. П.2 приведен набор логических операций, допустимых в VBA-программах: ÒÀÁËÈÖÀ Ï.2. ËÎÃÈ×ÅÑÊÈÅ ÎÏÅÐÀÖÈÈ VBA

Операция

Описание

Not And Or Xor Imp Eqv

Логическое отрицание Логическое "И" Логическое "ИЛИ" Исключительное "ИЛИ" Импликация Эквивалентность

Îïåðàöèè ñðàâíåíèÿ Операции сравнения используются для сравнения данных. Данные могут быть как числовые и строковые, так и объектные. Эти операции можно выполнять над переменными, константами и выражениями. Результатом любой операции сравнения всегда является логическое значение True или False. Операции сравнения допустимы только в отношении однородных данных. При сравнении строк используется порядок символов в кодовой странице. При сравнении плавающего значения с плавающим двойной точности последнее будет округлено. В табл. П.3 приведен набор операций сравнения, допустимых в VBA-программах: ÒÀÁËÈÖÀ Ï.3. ÎÏÅÐÀÖÈÈ ÑÐÀÂÍÅÍÈß VBA

Операция

Описание


= =

Is Like

Меньше Меньше или равно Больше Больше или равно Равно Не равно Идентичность объектов или соответствие типу Соответствие маске

Îïåðàöèÿ Is Эта операция применима к объектным переменным (ссылкам на объекты). Результат будет равным True только в том случае, когда обе переменные указывают на один и тот же объект. Если вторым операндом является идентификатор типа, то операция Is возвращает значение True в том

276 Приложение. VBA, как язык программирования: данные, синтаксис и функции случае, когда объект, на который ссылается объектная переменная (первый операнд) принадлежит к указанному типу (второй операнд). Например, следующий макрос может определить, существует ли в рабочей книге лист с именем “Лист1”: Dim MyObject As Object ... Set MyObject = Sheets("Лист1") If MyObject Is Nothing Then MsgBox("В данной книге нет такого листа") End If Îïåðàöèÿ Like Результат операции Like — логическое значение, показывающее, соответствует строка заданной маске, или нет. В табл. П.4 приведен набор маскирующих символов, допустимых в VBAпрограммах. ÒÀÁËÈÖÀ Ï.4. ÎÏÅÐÀÖÈÈ ÑÐÀÂÍÅÍÈß VBA

Маскирующий символ

Назначение

? *

Произвольный символ в данной позиции Произвольное число произвольных символов, начинающихся в данной позиции Произвольная цифра в данной позиции Один из символов, содержащихся в списке Любой символ, кроме содержащихся символов в списке

# […] [!…]

Директивы Option Compare Binary и Option Compare Text При помощи директивы Option Compare Binary можно задать режим бинарного сравнения операндов, то есть сравнения на основе совпадения числовых значений байтов. Режим текстового сравнения задается при помощи директивы Option Compare Text. При этом различие между заглавными и строчными литерами не принимается во внимание. Примеры использования операции Like Dim MySearchValue As String Dim MyResult As Boolean ... MySearchValue = "Иванов, год 1990" Попробуем теперь сравнить строку, содержащуюся в переменной MySearchValue с различными масками, используя операцию Like и помещая результат сравнения в логическую переменную MyResult: MyResult = MySearchValue Like "Ив???в, *" В переменной MyResult содержится значение True. MyResult = MySearchValue Like "*1991" В переменной MyResult содержится значение False. MyResult = MySearchValue Like "*, год 199#"

Синтаксические конструкции языка VBA 277 В переменной MyResult содержится значение True. MyResult = MySearchValue Like "И[вбд]ано[вк], *" В переменной MyResult содержится значение True. MyResult = MySearchValue Like "Ив[ае]н[ою]к, *" В переменной MyResult содержится значение False.

Îïåðàöèè ñî ñòðîêàìè Объединение строковых значений можно выполнить при помощи знаков “+” или “&”. Оба способа идентичны по сути выполняемого действия, однако, отличаются по некоторым сопутствующим обстоятельствам, которые могут сопровождать их использование. Рассмотрим следующий код: Dim MyString As String MyString = “Раз, ” MyString = MyString + “два, ” MyString = MyString & “три.” MsgBox MyString Нетрудно убедиться, что в переменной MyString содержится значение “Раз, два, три.” Обычно не рекомендуют использовать символ “+” по той причине, что этим же символом обозначается операция арифметического сложения. Если использовать данные типа Variant и не указывать явно тип переменных, которыми предполагается оперировать, то привычка использовать знак “+” для объединения строк может сыграть с программистом злую шутку. Например, думая, что объединяются строки “1” и “1”, на самом деле получится арифметическая сумма — число 2 вместо строки “11”. Однако символ “&”, который обычно рекомендуют использовать вместо символа “+” для объединения строк, также не лишен некоторого коварства. Дело в том, что ради совместимости со старыми вариантами языка BASIC, в Visual Basic поддерживается “древний” способ сокращенного указания типа переменных. В частности, символ “&” в конце имени переменной соответствует типу Long. Если пропустить пробел перед “&”, то конструкция будет воспринята компилятором, как объявление целочисленной переменной. ← см. также в этой главе раздел “Long”.

Óïðàâëÿþùèå êîíñòðóêöèè ÿçûêà Под управляющими конструкциями понимают обычно операторы (инструкции) и встроенные функции языка, при помощи которых осуществляется управление ходом выполнения программы. Сюда относят циклы разного рода, условные и безусловные переходы, а также конструкции ветвления (условные операторы), благодаря которым управление получает тот или иной фрагмент исходного кода.

Ïåðåõîä ïî ìåòêå Îïåðàòîð GoTo Оператор перехода по метке GoTo — самый, наверное, старинный способ передачи управления в программе. С наступлением эпохи структурного программирования этот оператор был предан анафеме, как главный источник ошибок и проблем при отладке программ, и использование его стало признаком дурного вкуса. Но все же, встречаются иногда ситуации, когда применение без-

278 Приложение. VBA, как язык программирования: данные, синтаксис и функции условного перехода GoTo может быть оправданным. Например, при организации обработки ошибок необходимо выполнить безусловный переход к фрагменту кода, обрабатывающему ошибку: ... On Error GoTo MyError1 ... ... Exit Sub MyError1: MsgBox "Произошла ошибка 1" End Sub Îïåðàòîð GoSub…Return Другой вариант безусловного перехода представлен оператором GoSub. Этот оператор также передает управление на заданную метку, однако, затем, когда в выполняемом коде встретится оператор Return, управление вернется к строке кода, следующей за GoSub. Такой механизм используют для вызова подпрограмм. Например: ... GoSub MySubSub1 ... GoSub MySubSub2 ... ... Exit Sub MySubSub1: MsgBox "Вызвана подпрограмма MySubSub1" Return MySubSub2: MsgBox "Вызвана подпрограмма MySubSub2" Return End Sub

Îïåðàòîðû öèêëà Циклические конструкции разного рода давно уже стали необходимым инструментом при выполнении массовых операций над данными. Существует несколько классических циклов, каждый из которых характеризуется определенными особенностями. При решении конкретной задачи следует выбрать тот вариант цикла, который наилучшим образом для этого подходит. Öèêë While … Wend Заключенная между строками While и Wend группа операторов выполняется циклически до тех пор, пока выражение в строке While возвращает значение True. Если условие не выполняется уже при входе в цикл, тело цикла не будет выполнено, а управление программой будет передано оператору, следующему за оператором Wend. Вот как, например, можно было бы организовать “сканирование” диапазона 20...100 в обратном направлении, от 100 к 20: Dim Counter As Integer ... Counter = 100 While Counter >= 20 ... Counter = Counter - 1 Wend

Синтаксические конструкции языка VBA 279 Этот цикл будет выполнен 81 раз. Öèêë Do … Loop Это наиболее универсальный вариант цикла, подходящий для решения любых циклических задач. Циклическая конструкция Do…Loop существует в двух вариантах, отличающихся способом задания условия выхода из цикла. Условие While (вариант Do While...) означает “выполнять, пока...”. Условие Until (вариант Do Until...) означает “выполнять, пока не...”. Заключенная между строками Do и Loop группа операторов выполняется циклически, пока условие While сохраняет значение True, или пока условие Until не примет значение True. Например: Do While ... ... If ... Then Exit Do End If ... Loop В любом месте цикла можно использовать оператор Exit Do для досрочного выхода их цикла при выполнении какого-то дополнительного условия. Проверку выполнения условия можно поместить в начало или в конец цикла. В этом случае допустимы следующие варианты Do While [условие] ... Loop, Do Until [условие] ... Loop, Do ... Loop While [условие] и Do ... Loop Until [условие]. Например: Do ... If ... Then Exit Do End If ... Loop Until ... Вот как, например, цикл Do Until используют для чтения из текстового файла с подсчетом числа прочитанных строк (условие Until EOF означает “пока не конец файла”): Dim DatRec(1500) As String Dim I As Integer I = 1 Open "C:\MyText.txt" For Input As #1 ... Do Until EOF(1) Input #1, DatRec(I) I = I + 1 Loop Close #1 Öèêë For … Next Количество проходов цикла определяется начальным и конечным значениями счетчика, а также значением шага. Если шаг цикла не указан, то он будет принят равным 1. Следующий цикл будет выполнен 100 раз, причем счетчик цикла (переменная I) на каждом проходе будет увеличивать свое значение на 1:

280 Приложение. VBA, как язык программирования: данные, синтаксис и функции For I=1 To 100 ... Next I Следующий цикл будет выполнен 10 раз, причем счетчик цикла (переменная I) на каждом проходе будет увеличивать свое значение на 10: For I=10 To 100 Step 10 ... Next I Отсчет можно производить и в обратном направлении, для этого значение шага должно быть отрицательным: For I=100 To 10 Step -10 ... Next I Öèêë For Each … Next При помощи конструкции For...Each можно организовать перебор всех объектов, принадлежащих к некоторому семейству. На каждом проходе переменная цикла будет содержать в себе очередной объект семейства. Доступ к свойствам и методам объекта при этом становится возможным через имя переменной цикла. Если объявлена объектная переменная MyWord: Dim MyWord As Object то перебор всех слов в текущем документе можно организовать следующим образом: For Each MyWord In ActiveDocument.Words ... Next MyWord

Ïðèíÿòèå ðåøåíèé: îïåðàòîðû âåòâëåíèÿ (óñëîâíûå êîíñòðóêöèè) Для принятия решений в процессе выполнения программы используют разнообразные условные конструкции. Подобно циклам, многие из них взаимозаменяемы, — в сущности, любые виды принятия решений (так же, как и любые циклические конструкции) можно реализовать при помощи простейшего оператора If ... Then ... Else ... End If. Но зачастую выбор подходящей конструкции значительно упрощает работу программиста. Такие конструкции называют иногда операторами ветвления, поскольку они обозначают разветвляющиеся пути выполнения программы. Ôóíêöèÿ Switch Функция-переключатель Switch служит для присваивания одного из значений при выполнении одного из условий. Функция Switch возвращает первое значение, если истинным окажется первое условие, второе значение, если истинным окажется второе условие и т.д. Проверка условий выполняется слева направо. Например: Dim MyNum As Integer Dim MyText As String MyNum = ... MyText = Switch(MyNum 11, "O.K.",…)

Синтаксические конструкции языка VBA 281 MsgBox MyText Если ни одно из условий в списке не выполнилось, функция возвращает значение Null. Независимо от того, какое по счету условие выполнилось, функция Switch проверяет все условия в списке, и в этом заключается ее существенное отличие от других условных конструкций. Ôóíêöèÿ Choose Эта функция во всем похожа на Switch — отличие заключается в том, что выражениепереключатель указывается один раз в начале списка вариантов. Функция возвращает то значение из списка, порядковый номер которого соответствует значению выражения-переключателя. Например: Dim KodKlienta As Integer Dim Klient As String KodKlienta = 2 Klient = Choose(KodKlienta, "Иванов", "Петров", "Сидоров") В строковой переменной Klient в результате будет содержаться значение “Петров”. Если значение выражения-переключателя не соответствует числу элементов в списке, функция вернет значение Null. Ôóíêöèÿ IIf Возвращает одно из двух значений в зависимости от логического значения, которое примет выражение-условие. Например: Dim MyNum As Integer Dim MyText As String MyNum = ... MyText = IIf(MyNum