1,082 36 9MB
English Pages 949 Year 2005
Professional Excel Development: The Definitive Guide to Developing Applications Using Microsoft® Excel and VBA® By Stephen Bullen, Rob Bovey, John Green Publisher: Addison Wesley Professional Pub Date: February 01, 2005 ISBN: 0-321-26250-6 Table of Contents • Index •
Pages: 936
• Examples
Finally, t here's a book t hat t reat s Excel as t he powerful developm ent plat form it really is, and covers every facet of developing com m ercialqualit y Excel applicat ions. This is not a book for beginners. Writ ing for professional developers and t rue Excel expert s, t he aut hors share insider's knowledge t hey've acquired building Excel applicat ions for m any of t he world's largest com paniesincluding Microsoft . Professional Excel Developm ent dem onst rat es how t o get t he ut m ost from Excel, addressing everyt hing from applicat ion archit ect ures t hrough worksheet and userform design, chart ing, debugging, error handling and opt im izing perform ance. Along t he way, t he aut hors offer best pract ices for every t ype of Excel developm ent , from building add- ins t hrough int eract ing wit h XML Web services. Coverage includes Building add- ins t o provide new Excel funct ions Designing effect ive worksheet s, userform s and ot her user int erface elem ent s Leveraging Excel's powerful dat a analysis feat ures Creat ing sophist icat ed cust om chart s Handling errors, debugging applicat ions and opt im izing per for m ance Using class m odules and int erfaces t o creat e cust om obj ect s Underst anding Windows API calls: when t o use t hem , and how t o m odify t hem Adding worksheet funct ions wit h C- based XLLs Program m ing wit h dat abases Cont rolling ext ernal applicat ions from Excel
I nt egrat ing wit h Visual Basic 6, VB.NET and Visual St udio Tools for Office Using XML t o im port and export dat a and com m unicat e wit h Web services Providing help, securing, packaging and dist ribut ing The accom panying CD- ROM cont ains t he book's sam ple t im esheet applicat ion at every st age of const ruct ion, wit h det ailed code com m ent s. I t also includes m any exam ples of t he concept s int roduced in each chapt er and a collect ion of t he aut hors' Excel developm ent ut ilit ies.
Professional Excel Development: The Definitive Guide to Developing Applications Using Microsoft® Excel and VBA® By Stephen Bullen, Rob Bovey, John Green Publisher: Addison Wesley Professional Pub Date: February 01, 2005 ISBN: 0-321-26250-6 Table of Contents • Index •
Pages: 936
• Examples
Copyright Praise for Professional Excel Development Acknowledgments About the Authors Stephen Bullen Rob Bovey John Green Chapter 1. Introduction About This Book The Excel Developer Excel as an Application Development Platform Structure Examples Supported Versions Typefaces On the CD Help and Support Feedback Chapter 2. Application Architectures Concepts Conclusion Chapter 3. Excel and VBA Development Best Practices Naming Conventions Best Practices for Application Structure and Organization General Application Development Best Practices Conclusion Chapter 4. Worksheet Design Principles of Good Worksheet UI Design Program Rows and Columns: The Fundamental UI Design Technique Defined Names Styles
User Interface Drawing Techniques Data Validation Conditional Formatting Using Controls on Worksheets Practical Example Conclusion Chapter 5. Function, General and Application-Specific Add-ins The Four Stages of an Application Function Library Add-ins General Add-ins Application-Specific Add-ins Practical Example Conclusion Chapter 6. Dictator Applications Structure of a Dictator Application Practical Example Conclusion Chapter 7. Using Class Modules to Create Objects Creating Objects Creating a Collection Trapping Events Raising Events Practical Example Conclusion Chapter 8. Advanced Command Bar Handling Command Bar Design Table-Driven Command Bars Putting It All Together Loading Custom Icons from Files Hooking Command Bar Control Events Practical Example Conclusion Chapter 9. Understanding and Using Windows API Calls Overview Working with the Screen Working with Windows Working with the Keyboard Working with the File System and Network Practical Examples Conclusion Chapter 10. Userform Design and Best Practices Principles Control Fundamentals Visual Effects Userform Positioning and Sizing Wizards
Dynamic Userforms Modeless Userforms Control Specifics Practical Examples Conclusion Chapter 11. Interfaces What Is an Interface? Code Reuse Defining a Custom Interface Implementing a Custom Interface Using a Custom Interface Polymorphic Classes Improving Robustness Simplifying Development A Plug-in Architecture Practical Example Conclusion Chapter 12. VBA Error Handling Error-Handling Concepts The Single Exit Point Principle Simple Error Handling Complex Project Error Handler Organization The Central Error Handler Error Handling in Classes and Userforms Putting It All Together Practical Example Conclusion Chapter 13. Programming with Databases An Introduction to Databases Designing the Data Access Tier Data Access with SQL and ADO Further Reading Practical Example Conclusion Chapter 14. Data Manipulation Techniques Excel's Data Structures Data Processing Features Advanced Functions Conclusion Chapter 15. Advanced Charting Techniques Fundamental Techniques VBA Techniques Conclusion Chapter 16. VBA Debugging Basic VBA Debugging Techniques The Immediate Window (Ctrl+G)
The Call Stack (Ctrl+L) The Watch Window The Locals Window The Object Browser (F2) Creating and Running a Test Harness Using Assertions Debugging Shortcut Keys that Every Developer Should Know Conclusion Chapter 17. Optimizing VBA Performance Measuring Performance The PerfMon Utility Creative Thinking Macro-Optimization Micro-Optimization Conclusion Chapter 18. Controlling Other Office Applications Fundamentals The Primary Office Application Object Models Practical Example Conclusion Chapter 19. XLLs and the C API Why Create an XLL-Based Worksheet Function Creating an XLL Project in Visual Studio The Structure of an XLL The XLOPER and OPER Data Types The Excel4 Function Commonly Used C API Functions XLOPERs and Memory Management Registering and Unregistering Custom Worksheet Functions Sample Application Function Debugging the Worksheet Functions Miscellaneous Topics Additional Resources Conclusion Chapter 20. Combining Excel and Visual Basic 6 A Hello World ActiveX DLL Why Use VB6 ActiveX DLLs in Excel VBA Projects In-Process versus Out-of-Process Automating Excel From a VB6 EXE Practical Examples Conclusion Chapter 21. Writing Add-ins with Visual Basic 6 A Hello World Add-in The Add-in Designer Installation Considerations The AddinInstance Events
Command Bar Handling Why Use a COM Add-in? Automation Add-ins Practical Example Conclusion Chapter 22. Using VB.NET and the Visual Studio Tools for Office Overview How to Leverage the .NET Framework Managed Workbooks Managed Excel Add-ins Hybrid VBA/VSTO Solutions The VSTO Security Model The Big Issues Further Reading Practical Example Conclusion Chapter 23. Excel, XML and Web Services XML Web Services Practical Example Conclusion Chapter 24. Providing Help, Securing, Packaging and Distributing Providing Help Securing Packaging Distributing Conclusion Index
Copyright Many of t he designat ions used by m anufact urers and sellers t o dist inguish t heir product s are claim ed as t radem arks. Where t hose designat ions appear in t his book, and t he publisher was aware of a t radem ark claim , t he designat ions have been print ed wit h init ial capit al let t ers or in all capit als. The aut hors and publisher have t aken care in t he preparat ion of t his book, but m ake no expressed or im plied warrant y of any kind and assum e no responsibilit y for errors or om issions. No liabilit y is assum ed for incident al or consequent ial dam ages in connect ion wit h or arising out of t he use of t he inform at ion or program s cont ained herein. The publisher offers excellent discount s on t his book when ordered in quant it y for bulk purchases or special sales, which m ay include elect ronic versions and/ or cust om covers and cont ent part icular t o your business, t raining goals, m arket ing focus, and branding int erest s. For m ore inform at ion, please cont act : U. S. Corporate and Governm ent Sales (800) 382-3419 cor psales@pear sont echgr oup.com For sales out side t he U. S., please cont act : I nternational Sales int er nat ional@pear soned.com Visit us on t he Web: w w w .aw pr ofessional.com Library of Congress Cat alog Num ber: 2004114575 Copyright © 2005 Pearson Educat ion, I nc. All right s reserved. Print ed in t he Unit ed St at es of Am erica. This publicat ion is prot ect ed by copyright , and perm ission m ust be obt ained from t he publisher prior t o any prohibit ed reproduct ion, st orage in a ret rieval syst em , or t ransm ission in any form or by any m eans, elect ronic, m echanical, phot ocopying, recording, or likewise. For inform at ion regarding perm issions, writ e t o: Pearson Education, I nc. Rights and Contracts Departm ent One Lake Street Upper Saddle River, NJ 07458 Text print ed in t he Unit ed St at es on recycled paper at Phoenix BookTech in Hagerst own, Maryland. First print ing, February, 2005
Praise for Professional Excel Development " Think you know Microsoft Excel? Think again. This book covers int erm ediat e ( class m odules, dict at or applicat ions, et c.) t o advanced t opics like XLLs, C API s and Web Services. I t offers plent y of easy t o underst and code list ings t hat show exact ly what t he aut hors are t rying t o convey wit hout forcing t he readers t o follow st ep- by- st ep." D e e pa k Sh a r m a , Sr . Syst e m s Spe cia list , Ta t a I n fot e ch Lt d. " This book t akes off where ot her Excel books st op. I t covers Excel program m ing beyond VBA and looks at t he professional issuessecurit y, dist ribut ion, working wit h dat abasesusing VB, VBA.NET and Windows API calls. The aut hors' dept h and pract ical experience shows in t he det ails. They explain com plex issues clearly, describe best pract ices and point out t raps t o av oid." Sh a u n a Ke lly, M icr osoft Office M VP, w w w .Sh a u n a Ke lly.com " The approach of following an applicat ion's developm ent is very effect ive in developing t he concept s as t he chapt ers unfold. The pract ical, working exam ples used are relevant t o m any professional program m ers." Ja n Ka r e l Pie t e r se , JKP Applica t ion D e ve lopm e n t Se r vice s, w w w .j k p- a ds.com " This book st ands out . While t here are plent y of Excel books, I am not aware of any organized in t his way. I nform at ion on .NET, and C, as well as ot her unique and useful chapt ers m akes t his a great offering." Ke n Blu t t m a n , Au t h or of D e ve lopin g M icr osoft Office Solu t ion s " This book explains difficult concept s in det ail. The aut hors provide m ore t han one m et hod for com plex developm ent t opics, along wit h t he advant ages and disadvant ages of using t he various m et hods described. They have m y applause for t he incorporat ion of developm ent best pr act ices." Be t h M e lt on , M icr osoft Office M VP
Acknowledgments First and forem ost , t his book would never have been writ t en wit hout t he support of our part ners and fam ilies, who have graciously put up wit h our insat iable com put er habit s and m any lat e night s over t he past year. Neit her would it have been done wit hout our dogs, who kept our feet warm while we worked and forced us t o get out of t he house at least once each day. We all owe a debt of grat it ude t o t he Excel group at Microsoft , past and present , for m aking Excel t he am azing developm ent plat form it is t oday. I t is t heir dedicat ion and com m it m ent t o us t hat m akes Excel applicat ion developm ent possible and enj oyable. They have repeat edly dem onst rat ed t heir willingness t o list en t o and im plem ent our suggest ions over t he years. There are m any people we want t o t hank at Addison- Wesley Professional, part icularly Am y Fleischer for bringing us t oget her, St ephane Thom as, Ebony Haight and Joan Murray for t heir support while writ ing t he book, Krist y Hart for st eering us t hrough t he product ion process and Curt Johnson for get t ing it on t he shelves. The qualit y of a t echnical book depends as m uch on t he reviewers as t he aut hors, so we want t o t hank all our t echnical reviewers. Most of your suggest ions were im plem ent ed. At t he risk of offending t he ot hers, we would part icularly like t o t hank Dick Kusleika and John Pelt ier for t he qualit y and rigor of t heir reviews and Bet h Melt on for finding num erous errors nobody else spot t ed. Last ly, we want t o t hank you for buying t his book. Please t ell us what you t hink about it , eit her by e- m ail or by writ ing a review at Am azon.com . Thank you, St ephen Bullen Rob Bovey John Green
About the Authors St ephen Bullen Rob Bovey John Green
Stephen Bullen st ephen@oalt d.co.uk St ephen Bullen lives in Woodford Green, London, England, wit h his part ner Clare, daught er Becky and t heir dog, Fluffy. A graduat e of Oxford Universit y, St ephen has an MA in Engineering, Econom ics and Managem ent , providing a unique blend of bot h business and t echnical skills. He has been providing Excel consult ing and applicat ion developm ent services since 1994, originally as an em ployee of Price Wat erhouse Managem ent Consult ant s and since 1997 as an independent consult ant t rading under t he nam e of Business Modelling Solut ions Lim it ed. I n Sept em ber 2004, BMS changed it s nam e t o Office Aut om at ion Lim it ed. I f you would like t o m ake use of St ephen's services, please cont act him at st ephen@oalt d.co.uk. The Office Aut om at ion Web sit e, w w w .oalt d.co.uk, provides a num ber of helpful and int erest ing ut ilit ies, exam ples, t ips and t echniques t o help in your use of Excel and developm ent of Excel applicat ions. St ephen cont ribut ed chapt ers t o John Green's Excel 2000 VBA Program m er's Reference and coaut hored t he sequel, Excel 2002 VBA Program m er's Reference ( bot h published by Wrox Press) . I n addit ion t o his consult ing and writ ing assignm ent s, St ephen act ively support s t he Excel user com m unit y in Microsoft 's peer- t o- peer support newsgroups. I n recognit ion of his knowledge, skills and cont ribut ions, Microsoft has awarded him t he t it le of Most Valuable Professional each year since 1996.
Rob Bovey r obbov ey @appspr o.com Rob Bovey is president of Applicat ion Professionals, a soft ware developm ent com pany specializing in Microsoft Office, Visual Basic, and SQL Server applicat ions. He brings m any years' experience creat ing financial, account ing and execut ive inform at ion syst em s for corporat e users t o Applicat ion Professionals. You can visit t he Applicat ion Professionals Web sit e at w w w .appspr o.com . Rob developed several add- ins shipped by Microsoft for Microsoft Excel, co- aut hored t he Micr osoft Excel 97 Developers Kit and cont ribut ed t o t he Excel 2002 VBA Program m er's Reference. He earned his Bachelor of Science degree from The Rochest er I nst it ut e of Technology and his MBA from t he Universit y of Nort h Carolina at Chapel Hill. He is a Microsoft Cert ified Syst em s Engineer ( MCSE) and a Microsoft Cert ified Solut ion Developer ( MCSD) . Microsoft has awarded him t he t it le of Most Valuable Professional each year since 1995. He current ly resides in Edm onds, Washingt on, wit h his wife Michelle, and t heir t wo black labs, Jasper and Jade.
John Green gr eenj @bigpond.net .au John Green lives and works in Sydney, Aust ralia, as an independent com put er consult ant , specializing in int egrat ing Excel, Access, Word and Out look using VBA. He has m ore t han 30 years of com put ing experience, a Chem ical Engineering degree and an MBA. He wrot e his first program s in FORTRAN, t ook a part in t he evolut ion of specialized planning languages on m ainfram es and, in t he early 1980s, becam e int erest ed in spreadsheet syst em s, including 1- 2- 3 and Excel. John est ablished his com pany, Execuplan Consult ing, in 1980, developing com put er- based planning applicat ions and t raining users and developers. John has had regular colum ns in a num ber of Aust ralian m agazines and has cont ribut ed chapt ers t o a num ber of books, including Excel Expert Solut ions and Using Visual Basic for Applicat ions 5, published by Que. He is t he principal aut hor of Excel 2000 VBA Program m er's Reference and it s subsequent edit ions, published by Wrox Press. Since 1995 he has been accorded t he st at us of Most Valuable Professional by Microsoft for his cont ribut ions t o t he Com puServe Excel forum and MS I nt ernet newsgroups.
Chapter 1. Introduction About This Book The Excel Developer Excel as an Applicat ion Developm ent Plat form St r uct ur e Exam ples Support ed Versions Typefaces On t he CD Help and Support Feedback
About This Book Microsoft Excel is m uch, m uch m ore t han j ust a spreadsheet . Since t he int roduct ion of t he Visual Basic Edit or in Excel 97 and t he im proved st abilit y of Excel 2000, it has becom e a respect ed developm ent plat form in it s own right . Applicat ions writ t en using Excel are now oft en found alongside t hose writ t en using Visual Basic, C+ + , Java, .NET and so on, as part of m any corporat ions' core suit e of business- crit ical applicat ions. I ndeed, Excel is oft en used for t he client end of Web- based applicat ions, m ade part icularly easy wit h Excel 2003's XML im port / export feat ur es. Unfort unat ely, Excel is st ill all t oo oft en t hought of as a hobbyist plat form t hat people develop spreadsheet - based applicat ions in t heir spare t im e t o help out wit h t heir day j ob. A brief look at t he shelves of any bookst ore seem s t o confirm t hat opinion. Alt hough t here are m yriad t it les explaining how t o use Excel and num erous t it les about Excel and VBA, none provide an overall explanat ion of how t o develop professional- qualit y Excel- based applicat ions. This is t hat book. Whereas all t he ot her m aj or languages seem t o have a de fact o st andard t ext t hat explains t he com m only agreed best pract ices for archit ect ing, designing and developing applicat ions in t hat language, Excel does not . This book aim s t o fill t hat gap. All t hree aut hors are professional Excel developers who run our own com panies developing Excelbased applicat ions for client s ranging from individuals t o t he largest m ult inat ional corporat ions. This book det ails t he approaches we use when designing, developing, dist ribut ing and support ing t he applicat ions we writ e for our client s. This is not a beginner- level book. We assum e t hat t he reader will have read and ( m ost ly) underst ood our Excel 2000/ 2002 VBA Program m er's Reference, John Walkenbach's Excel Power Pr ogr am m ing, or sim ilar t it les.
The Excel Developer Excel developers can be divided int o five general cat egories, based on t heir experience and knowledge of Excel and VBA. To varying degrees, t his book has som et hing t o offer each of t hem , but wit h a focus on t he m ore advanced t opics. Put t ing yourself int o one of t hese cat egories m ight help you decide whet her t his is t he right book for you. The basic Ex ce l u se r probably doesn't t hink of him self as a developer at all. To basic users, Excel is no m ore t han a t ool t o help t hem get on wit h t heir j ob. They st art off using Excel worksheet s as a handy place t o st ore list s or perform sim ple repet it ive calculat ions. As t hey discover m ore of Excel's funct ionalit y, t heir workbooks becom e m ore com plex and st art t o include lot s of worksheet funct ions, pivot t ables and chart s. There is lit t le in t his book for t hese people; alt hough Chapt er 4 Worksheet Design det ails t he best pract ices t o use when designing and laying out a worksheet for dat a ent ry, Chapt er 14 Dat a Manipulat ion Techniques explains how t o st ruct ure a worksheet and which funct ions and feat ures t o use t o m anipulat e t heir list s, and Chapt er 15 Advanced Chart ing Techniques, explains how t o get t he m ost from Excel's chart engine. The t echniques suggest ed in t hese chapt ers should help t he basic Excel user avoid som e of t he pit falls oft en encount ered as t heir experience and t he com plexit y of t heir spreadsheet s increases. The Ex ce l pow e r u se r has a wide underst anding of Excel's funct ionalit y, knows which t ool or funct ion is best t o use in a given sit uat ion, creat es com plex spreadsheet s for his own use and is oft en called on t o help develop colleagues' spreadsheet s or t o ident ify why colleagues' spreadsheet s do not work as int ended. Occasionally power users include sm all snippet s of VBA t hey found on t he I nt ernet or creat ed using t he m acro recorder, but st ruggle t o adapt t he code t o t heir needs. As a result , t hey produce code t hat is unt idy, slow and hard t o m aint ain. Alt hough t his book is not a VBA t ut orial, t he power user has m uch t o gain from following t he best pract ices we suggest for bot h worksheet s and code m odules. Most of t he chapt ers in t he book are relevant t o power users who have an int erest in im proving t heir Excel and VBA developm ent skills. The VBA de ve lope r m akes ext ensive use of VBA code in his workbooks ( oft en t oo m uch) . VBA developers are t ypically eit her power users who have st art ed t o learn VBA t oo early or Visual Basic 6 developers who have swit ched t o Excel VBA developm ent . Alt hough t hey m ight be very proficient at VBA, t hey believe every problem m ust have a VBA solut ion and lack sufficient knowledge of Excel t o m ake t he best use of it s feat ures. Their solut ions are oft en cum bersom e, slow and m ake poor use of Excel's obj ect m odel. This book has m uch t o offer VBA developers t o im prove t heir use of Excel it self, including explaining how t o archit ect Excel- based applicat ions, t he best pract ices for designing worksheet s and how t o use Excel's feat ures for t heir dat a ent ry, analysis and present at ion. This book also seeks t o im prove t heir Excel VBA developm ent skills by int roducing advanced coding t echniques, det ailing VBA best pract ices and explaining how t o im prove t heir code's perform ance. The Ex ce l de ve lope r has realized t he m ost efficient and m aint ainable applicat ions are t hose t hat m ake t he m ost of Excel's own funct ionalit y, augm ent ed by VBA when appropriat e. Excel developers are confident developing Excel- based applicat ions for t heir colleagues t o use or as part of an inhouse developm ent t eam . Their undoubt ed knowledge of Excel is put t o good use in t heir applicat ions, but it also const rains t heir designs, and t hey are reluct ant t o use ot her languages and applicat ions t o augm ent t heir Excel solut ions. They have probably read John Walkenbach's Excel
Power Program m ing and/ or our own Excel 2000/ 2002 VBA Program m er's Reference and need a book t o t ake t hem t o t he highest level of Excel applicat ion developm ent t hat of t he professional developer. This is t hat book. The pr ofe ssion a l Ex ce l de ve lope r designs and develops Excel- based applicat ions and ut ilit ies for client s or em ployers t hat are robust , fast , easy t o use, m aint ainable and secure. Excel form s t he core of t heir solut ions, but t hey include any ot her applicat ions and languages t hat are appropriat e. For exam ple, t hey m ight use t hird- part y Act iveX cont rols; aut om at e ot her applicat ions; use Windows API calls; use ADO t o connect t o ext ernal dat abases, C/ C+ + for fast cust om worksheet funct ions, VB6 or VB.Net for creat ing t heir own obj ect m odels and securing t heir code; and XML for sharing dat a over t he I nt ernet . This book t eaches all t hose skills. I f you are already a professional Excel developer, you know t hat learning never st ops and will appreciat e t he knowledge and best pract ices present ed in t his book by t hree of your peers.
Excel as an Application Development Platform I f we look at Excel as a developm ent plat form and not j ust a spreadsheet , we can break it down int o five fundam ent al com ponent s we can use for our applicat ions: The worksheet , chart s and so on, used as a user int erface and present at ion layer for dat a ent ry and report ing The worksheet , used as a sim ple dat a st ore for list s, t ables and ot her inform at ion used by our applicat ion VBA, Excel's program m ing language and form s engine The worksheet , used as a declarat ive program m ing language for high- perform ance num eric pr ocessing The Excel obj ect m odel, allowing program m at ic cont rol of ( nearly) all of Excel's funct ionalit y, from bot h wit hin Excel and from out side it
The Worksheet as a Presentation Layer for Data Entry and Reporting When m ost people t hink about Excel, t hey t hink in t erm s of t yping num bers int o cells, having som e calculat ions updat e and seeing a result displayed in a different cell or on a chart . Wit hout necessarily t hinking in such t erm s, t hey are using t he worksheet as a user int erface for t heir dat a ent ry and report ing and are generally com fort able wit h t hese t asks. The in- cell edit ing, validat ion and form at t ing feat ures built in t o Excel provide an ext rem ely rich and com pelling dat a- ent ry experience, while t he chart ing, cell form at t ing and drawing t ools provide a present at ion- qualit y report ing m echanism . I t is hard t o im agine t he code t hat would be required if we t ried t o reproduce t he experience using t he form design t ools available in m ost ot her developm ent environm ent s, yet it 's t here wait ing for us t o use in our Excel- based applicat ions. The biggest problem we face is how t o add som e st ruct ure t o t he free- form grid of t he worksheet , t o present a sim ple and easy- t o- use int erface, while leveraging t he rich funct ionalit y Excel provides. Chapt er 4 Worksheet Design int roduces som e t echniques and best pract ices for developing worksheet - based dat a- ent ry form s, and Chapt er 15 Advanced Chart ing Techniques discusses using Excel's chart ing capabilit ies.
The Worksheet as a Simple Data Store What is a worksheet when it is never int ended t o be shown t o t he end user? At it s sim plest , it is no m ore t han a large grid of cells in which we can st ore j ust about anyt hing we want t onum bers, t ext , list s, t ables or pict ures. Most applicat ions use som e am ount of st at ic dat a or t ext ual or graphical resources; st oring t hat inform at ion in a worksheet m akes it bot h ext rem ely easy t o access using VBA and sim ple t o m aint ain. List s and t ables in worksheet s can direct ly feed Excel's dat a validat ion ( as shown in Chapt er 4 Worksheet Design) , great ly sim plify t he creat ion and m aint enance of
com m and bars (Chapt er 8 Advanced Com m and Bar Handling) , and enable us t o const ruct dynam ic userform s ( Chapt er 10 Userform Design and Best Pract ices) .
VBA: Excel's Programming Language We expect m ost readers of t his book t o have at least som e fam iliarit y wit h VBA. I f not , we suggest you read eit her our Excel 2000/ 2002 VBA Program m er's Reference or John Walkenbach's Excel Power Program m ing before cont inuing m uch furt her. Many people see t he A in VBA as m eaning t he language is som ehow less t han Visual Basic it self. I n fact , bot h VB6 and Office 2000 and above use exact ly t he sam e DLL t o provide t he keywords, synt ax and st at em ent s we program wit h. The only differences are t he obj ect s provided by t he runt im es ( t he VB runt im e vs. t he Excel obj ect s) , t he form s packages ( VB's " Ruby" form s vs. Office UserForm s) and t hat VB6 includes a com piler t o creat e EXEs and DLLs, whereas VBA is always int erpret ed at runt im e. I ndeed, t he Office Developer Edit ion ( pre- Excel 2003) includes t he sam e com piler VB6 uses, enabling us t o com pile ( sim ple) DLLs from wit hin t he Office Visual Basic Edit or. Most beginner and int erm ediat e VBA developers use VBA as a p u r e ly procedural language, wit h nearly all t heir code residing in st andard m odules. VBA also enables us t o creat e applicat ions using an obj ect - orient ed program m ing ( OOP) approach, in which class m odules are used t o creat e our own obj ect s. Chapt er 7 Using Class Modules t o Creat e Obj ect s and Chapt er 11 I nt erfaces explain how t o use VBA in t his m anner, while basic OOP concept s ( such as encapsulat ion) are used t hroughout t he book. Most of t his book is dedicat ed t o explaining advanced VBA t echniques and a professional approach t o applicat ion design and developm ent t hat can put using VBA in Excel on a par wit h, and som et im es in front of, using VB6 or VB.Net for applicat ion developm ent . We also show in Chapt er 20 Com bining Excel and Visual Basic 6 and Chapt er 22 Using VB.NET and t he Visual St udio Tools for Office t hat t he Excel developer can use t he best of bot h worlds, by com bining Excel, VB6 and/ or VB.Net in a seam less applicat ion.
The Worksheet as a Declarative Programming Language Consider t he following code:
dSales = 1000 dPrice = 10.99 dRevenue = dSales * dPrice
This code could quit e easily be a few lines of VBA. We give t he variable dSales a value of 1000, t he v ar iable dPrice a value of 10.99 and t hen calculat e t he revenue as sales t im es price. I f we change t he nam es of t he variables and adj ust t he spacing, t he sam e code could also be writ t en as follows:
D1 D2 D3
=1000 =10.99 =D1*D2
This preceding code looks m uch m ore like worksheet cell addresses and form ulas t han lines of VBA code, showing t hat a worksheet is in fact a program m ing language of it s own, if we choose t o t hink of it in t hose t erm s. The I F( ) worksheet funct ion is direct ly equivalent t o t he If...Then...Else VBA st at em ent , and t he j udicious use of circular references and it erat ion can be equivalent t o eit her t he For...Next or Do...Loop st ruct ures. I nst ead of st at ing a set of ope r a t ion s t hat are execut ed line by line, we " program " in t his language by st at ing a set of de cla r a t ion s ( by t yping form ulas and values int o worksheet cells) , in any order we want t o:
"D3 is the product of D1 and D2" "D1 has the value 1000" "D2 has the value 10.99"
To " run" t his program , Excel first exam ines all t he declarat ions and builds a " precedence t ree" t o ident ify which cells depend on t he result s of which ot her cells and t hereby det erm ine t he m ost efficient order in which t he cells m ust be calculat ed. The sam e precedence t ree is also used t o ident ify t he m inim um set of calculat ions t hat m ust be perform ed whenever t he value in a cell is changed. The result is a calculat ion engine t hat is vast ly m ore efficient t han an equivalent VBA program , and one t hat should be used whenever com plex num eric com put at ions are required in our applicat ions. Microsoft Excel ( and ot her spreadsheet program s) is unique am ong applicat ion developm ent plat form s in providing bot h a procedural ( VBA) and a declarat ive ( t he worksheet ) program m ing language. The m ost efficient Excel applicat ion is one t hat m akes appropriat e use of bot h t hese languages. I t is assum ed t he reader of t his book has som e knowledge of Excel and worksheet funct ions, so Chapt er 14 Dat a Manipulat ion Techniques focuses on using advanced worksheet funct ions ( including best - pract ice suggest ions for handling circular references) and Excel's ot her dat aanalysis feat ures.
The Excel Object Model Alt hough t he ot her four com ponent s of t he Excel plat form are invaluable in t he developm ent of applicat ions, it is probably t he richness of t he Excel obj ect m odel t hat provides t he m ost com pelling reason t o base our applicat ion developm ent on Excel. Alm ost everyt hing t hat can be done t hrough t he user int erface can also be done program m at ically by using t he obj ect s in t he Excel obj ect m odelaccessing t he list of num ber form at s and applying a digit al signat ure t o a workbook are perhaps t he m ost not able except ions. The vast array of funct ionalit y exposed by t hese obj ect s m akes highly com plex applicat ions fairly sim ple t o developit becom es m ore an issue of when and how t o efficient ly plug t he funct ionalit y t oget her t han t o develop t he funct ionalit y from scrat ch. This book does not at t em pt t o explore and docum ent all t he backwat ers of t he obj ect m odel, but inst ead m akes cont inual use of t he obj ect s in our applicat ion developm ent .
Structure Through t he course of t his book, we cover bot h t he concept s and det ails of each t opic and apply t hose concept s t o a t im esheet report ing and analysis applicat ion we build. The chapt ers are t herefore arranged approxim at ely in t he order in which we would design and develop an Excel applicat ion: Chapt er 2 discusses t he different st yles of applicat ion we m ight choose t o creat e. Chapt er 3 ident ifies som e general best pract ices for working wit h Excel and VBA, which are followed t hroughout t his book. Chapt er 4 explains how t o design and st ruct ure a worksheet for dat a ent ry. Chapt ers 5 and 6 int roduce t wo specific t ypes of applicat ion, t he add- in and t he dict at or applicat ion, which form t he basis of our t im esheet report ing and analysis suit e. Chapt ers 7 t hrough 13 discuss advanced t echniques for a range of VBA t opics. Chapt ers 14 and 15 explain how t o efficient ly ut ilize Excel's feat ures wit hin an applicat ion t o analyze dat a and present result s. Chapt ers 16 and 17 discuss t echniques for debugging and opt im izing VBA code. Chapt ers 18 t hrough 22 look out side of Excel, first by explaining how t o aut om at e ot her applicat ions, and t hen by explaining how t o int eract wit h Excel using C, Visual Basic and VB.Net . Chapt er 23 focuses on how Excel applicat ions can m ake use of t he I nt ernet and XML. Chapt er 24 com plet es t he developm ent by explaining how t o provide help and how t o secure, package and dist ribut e t he applicat ion.
Examples Throughout t he book, we illust rat e t he concept s and t echniques we int roduce by building a t im esheet dat a- ent ry, consolidat ion, analysis and report ing applicat ion. This applicat ion com prises a dat a- ent ry t em plat e t o be com plet ed by each em ployee, wit h t he dat a sent t o a cent ral locat ion for consolidat ion, analysis and report ing. The end of each chapt er present s a fully working exam ple of bot h part s of t he applicat ion included on t he CD, which grows st eadily m ore com plex as t he book progresses and t herefore will be relevant t o different t ypes of com panies. I n Chapt er 4 Worksheet Design, we st art wit h a very sim ple dat a- ent ry workbook and t he assum pt ion t hat each em ployee would e- m ail t he com plet ed file t o a m anager, who would analyze t he result s m anuallya t ypical sit uat ion for a com pany wit h perhaps 10 t o 20 em ployees. By t he end of t he book, t he dat a- ent ry workbook uses XML t o upload t he dat a t o a Web service, where it is st ored in a cent ral dat abase. The report ing applicat ion ext ract s t he dat a from t he dat abase, perform s various analyses and present s t he result s as report s in Excel worksheet s and chart s. Along t he way, we rewrit e som e of t he part s of t he applicat ion in a num ber of different ways, t o show how easy it can be t o include ot her languages and delivery m echanism s in our Excel- based applicat ions. Each chapt er m ay also include specific exam ples t o illust rat e key point s t hat it would be t oo art ificial t o include in our m ain applicat ion.
Supported Versions When developing an Excel applicat ion for a client , t heir upgrade policy will usually det erm ine which version of Excel we m ust use; very few client s will agree t o upgrade t heir deskt ops j ust so we can develop using t he lat est version, unless t here is a com pelling business requirem ent t hat can only be sat isfied by using feat ures t hat t he lat est version int roduces. There is so lit t le difference bet ween Excel 2000 and Excel 2003 t hat it is hard t o im agine such a business requirem ent . An ext rem ely unscient ific poll ( based on post ings t o t he Microsoft support newsgroups) seem s t o indicat e t he following approxim at e usage for each version: Excel 97
10%
Excel 2000
45%
Excel 2002
40%
Excel 2003
5%
There were a num ber of significant changes bet ween Excel 97 and Excel 2000 for t he applicat ion developer, including t he swit ch from VBA5 t o VBA6 and t he int roduct ion of m odeless userform s, int erfaces, COM Add- ins and support for ADO. We have t herefore decided t o use Excel 2000 as our lowest support ed version and developm ent plat form , wit h our applicat ions t est ed in t he lat er versions. Most of t he concept s det ailed in t his book apply equally t o Excel 97, but our exam ple t im esheet applicat ion uses feat ures Excel 97 does not support . Whenever we discuss a feat ure
Typefaces The following t ext st yles are used in t his book: Menu it em s and dialog t ext will be shown as Tools > Opt ions > Calculat ion > Manual, where t he > indicat es navigat ion t o a subm enu or dialog t ab.
Sub SomeCode() 'Code listings are shown like this 'With new or changed lines highlighted like this End Sub
Code wit hin a paragraph is shown as Application.Calculation = xlManual. Pat hs on t he CD are shown as \ Concept s\ Ch11 - I nt erfaces. URLs are shown as ht t p: / / w w w .oalt d.co.uk. I m port ant point s or em phasized words are shown bold and it alic, lik e t h is.
On the CD Most of t he code list ings shown in t his book are also included in exam ple workbooks on t he accom panying CD. For clarit y, t he code shown in t he book uses short er line lengt hs, a reduced indent set t ing, fewer in- code com m ent s and less error handling t han t he corresponding code in t he w or k book s. The CD has t hree m ain direct ories, cont aining t he following files: \ Tools cont ains a num ber of t ools and ut ilit ies developed by t he aut hors t hat we have found t o be invaluable during our applicat ion developm ent . The Must HaveTools.ht m file cont ains det ails about each of t hese t ools and links t o ot her t hird- part y ut ilit ies. \ Concept s has separat e subdirect ories for each chapt er, each one cont aining exam ple files t o support t he t ext of t he chapt er. For best result s, we suggest you have t hese workbooks open while reading t hrough t he corresponding chapt er. \ Applicat ion has separat e subdirect ories for each chapt er, each one cont aining a version of our t im esheet exam ple applicat ion suit e. Beginning wit h Chapt er 4 Worksheet Design, m ost chapt ers end wit h a Pract ical Exam ple sect ion, explaining t he changes t hat have been m ade t o t he t im esheet applicat ion t o im plem ent som e of t he concept s int roduced in t he chapt er.
Help and Support Quest ions about t he book it self ( such as m issing CDs, t ypos, errat a and so on) should be direct ed t o Addison- Wesley, at ht t p: / / w w w .aw pr ofessional.com / cont act us. Any errat a and correct ions will be post ed t o t he Addison- Wesley Web sit e at ht t p: / / w w w .aw pr ofessional.com / t it le/ 0321262506. By far t he best place t o go for help wit h any of your Excel developm ent quest ions, whet her relat ed t o t his book or not , are t he Microsoft support newsgroups' archives m aint ained by Google at ht t p: / / gr oups.google.com . A quick search of t he archives is alm ost cert ain t o find a quest ion sim ilar t o yours, already answered by one of t he m any professional developers who volunt eer t heir t im e helping out in t he newsgroups, including all t hree of t his book's aut hors. On t he rare occasions t hat t he archives fail t o answer your quest ion, you are welcom e t o ask it direct ly in t he newsgroups by connect ing a news reader ( such as Out look Express) t o m snew s.m icr osoft .com and select ing an appropriat e newsgroup, such as t hese: microsoft.public.excel.programming for VBA- relat ed quest ions microsoft.public.excel.worksheet.functions for help wit h worksheet funct ions microsoft.public.vsnet.vstools.office for help wit h Excel/ VB.Net int egrat ion issues microsoft.public.excel.misc for general Excel queries A num ber of Web sit es provide a great deal of inform at ion and free downloadable exam ples and ut ilit ies t arget ed t oward t he Excel developer, including t he following: ht t p: / / w w w .oalt d.co.uk ht t p: / / w w w .appspr o.com ht t p: / / w w w .j - w alk .com ht t p: / / w w w .cpear son.com ht t p: / / m sdn.m icrosoft .com / office
Feedback We have t ried very hard t o present t he inform at ion in t his book in a clear and concise m anner, explaining bot h t he concept s and det ails needed t o get t hings working and providing working exam ples of everyt hing we cover. We have t ried t o provide sufficient inform at ion t o enable you t o apply t hese t echniques in your own applicat ions, but wit hout get t ing bogged down in line- by- line explanat ions of ent ire code list ings. We would like t o t hink we have been successful in our at t em pt , but encourage you t o let us know what you t hink. Const ruct ive crit icism is always welcom e, as are suggest ions for t opics you t hink we m ay have overlooked. Please send an e- m ail t o one ( or all) of t he following: St ephen Bullen: st ephen@oalt d.co.uk Rob Bovey: r obbov ey @appspr o.com John Green: gr eenj @bigpond.net .au
Chapter 2. Application Architectures One of t he first decisions t o be m ade when st art ing a new proj ect is how t o st ruct ure t he applicat ion. This chapt er explains t he alt ernat ive archit ect ures we can use, t he sit uat ions where each is m ost applicable, and t he pros and cons of each choice.
Concepts The choice of where t o put t he code for an Excel applicat ion is rarely st raight forward. I n anyt hing but t he sim plest of sit uat ions, it is a considered t rade- off bet ween num erous fact ors, including t he follow ing: Com ple x it y How easy will t he chosen archit ect ure be t o creat e? Cla r it y How easy will it be for som eone ot her t han t he aut hor t o underst and t he applicat ion? D e ve lopm e n t How easy will it be t o m odify t he code, part icularly in a t eam environm ent ? Fle x ibilit y How easy is it t o add new feat ures? Re lia bilit y Can t he result s be relied upon? How easily can calculat ion errors be int roduced int o t he applicat ion? Robu st n e ss How well will t he applicat ion be able t o handle applicat ion errors, invalid dat a, and so fort h? Se cu r it y How easy will it be t o prevent unaut horized changes t o t he applicat ion? D e ploy m e n t How easy will it be t o dist ribut e t he applicat ion t o t he end user? Upda t e s How easy will it be t o updat e t he applicat ion aft er it has been dist ribut ed and st art ed t o be used? One of t he m ost fundam ent al t enet s of applicat ion design is t hat t he " program " elem ent s of t he applicat ion m ust be physically separat e from t he " dat a" it works on.
Codeless Applications The m ost basic applicat ion is one t hat only uses Excel's built - in funct ionalit y. Everybody creat es t his t ype of applicat ion wit hout knowing it , sim ply by using Excel. They are t ypically creat ed by beginning t o int erm ediat e Excel users who have not yet learned t o use VBA. All t he special form at t ing, validat ion, form ulas and so on are placed direct ly on t he sam e worksheet where dat a ent ry will be perform ed. There are som e m aj or issues wit h t his approach, which t oget her m ean t ot ally codeless applicat ions are rarely a good choice. Those who avoid using VBA m ay find t hat som e of t heir worksheet funct ions and dat a- validat ion crit eria get ext rem ely com plex and alm ost incom prehensible t o anyone ot her t han t he aut hor. The equivalent VBA will oft en be easier t o underst and. The sam e worksheet is usually used for dat a ent ry, analysis, and present at ion, oft en result ing in a clut t ered appearance ( usually wit h m ult it udes of different ly colored cells t o t ry t o ident ify t heir purpose) t hat is difficult t o underst and, is unint uit ive t o use and is alm ost im possible for anyone except t he aut hor t o m odify reliably.
Such applicat ions have t o rely on Excel's cell prot ect ion and password- prot ect ing t he worksheet t o prevent t he users m aking unaut horized changes t o t he form ulas, validat ion, form at t ing and so on. Worksheet passwords are not oriously easy t o break, and a sim ple copy and past e will wipe out any dat a validat ion t hat has been set up. These applicat ions are t herefore neit her secure nor robust . Wit hout any code, we are unable t o provide m uch assist ance t o t he user; we have t o rely on t hem t o do everyt hing t hem selvesand do it correct lyinst ead of providing reliable helper rout ines t hat aut om at e som e of t heir t asks. The m ore com plex t he applicat ion, t he less likely it is t hat all t he t asks will be perform ed correct ly. I f we consider a definit ion of what const it ut es a " program " t o be " anyt hing t hat isn't t he dat a," we see t hat all t he condit ional form at t ing, dat a validat ion, worksheet funct ions and so fort h are really part of t he " program ," so t hese applicat ions break t he basic t enet of keeping t he program and dat a physically separat e. Aft er t he end users have st art ed t o ent er dat a int o t heir worksheet , it is very difficult t o dist ribut e an updat ed worksheet t o t hem , wit hout losing t he dat a t hey have already ent ered. I n addit ion t o t he new version of t he worksheet , you would have t o eit her provide clear inst ruct ions explaining how t o copy t heir dat a across or writ e a conversion program t hat will copy t heir dat a from t he old t o t he new workbook. Codeless applicat ions are ideal for sim ple sit uat ions where m ost of t he following condit ions apply: There will only be one copy of t he workbook ( so any changes can be done direct ly t o t hat workbook) , or each copy of t he workbook will have a short lifet im e, such as a survey or dat acollect ion form t hat can be forgot t en about aft er it has been com plet ed and ret urned. I n each case, t he assum pt ion is t hat t he workbooks will not need up dat ing aft er t hey have been deploy ed. The end users will be m aint aining t he workbook t hem selves ( and don't know VBA) , or t he workbook will not require any m aint enance at all. The workbook m ight be copied and updat ed on different m achines ( alt hough only one at a t im e) , such as when t he workbook is t aken hom e for t he weekendbecause it is self- cont ained, it can easily be copied t o a floppy disk or e- m ailed. There are relat ively few rout ine or com plex t asks t o be perform ed t o m aint ain or analyze t he dat a. There are only a sm all num ber of end users, who can be well t rained t o ensure t he applicat ion is used correct ly and is not inadvert ent ly broken. A good exam ple of a codeless applicat ion is a sim ple survey or dat a- collect ion form t hat requires t he end user t o fill in t he det ails and e- m ail t he com plet ed workbook t o a cent ral address for consolidat ion and analysis. The m ain benefit of a codeless applicat ion in such a sit uat ion is t he avoidance of Excel's m acro securit y warnings and t he corresponding assurance t hat t here is not hing m alicious in t he file.
Self-Automated Workbooks A self- aut om at ed workbook is one in which t he VBA code is physically cont ained wit hin t he workbook it act s upon. Probably t he m ost com m on t ype of applicat ion, t he aut om at ion code can be
as sim ple as ensuring t he workbook always opens wit h Sheet 1 act ive or be as com plex as an ent ire applicat ion. This is usually t he first t ype of applicat ion a beginning VBA developer produces, built by adding num erous helper rout ines t o t he workbook t hat get progressively m ore com plex ( and usually cum bersom e) over t im e. Aft er VBA is int roduced t o t he workbook, we have m uch m ore flexibilit y in providing t he required funct ionalit y and can m ake a considered choice whet her t o use Excel's built - in funct ions or writ e our own equivalent s t o avoid som e of Excel's pit falls. For exam ple, Excel's dat a validat ion m ight not fire correct ly when ent ries are m ade in m ult iple cells at t he sam e t im e and is usually cleared when dat a is past ed ont o t he range. We can work around bot h t hese lim it at ions by t rapping t he Worksheet _Change event and perform ing our own validat ion in code, m aking t he worksheet m ore robust , reliable, and secure. The workbook and worksheet code m odules are provided for us by Excel t o hook whichever event s we want t o use, and any Act iveX cont rols we add t o t he worksheet are aut om at ically exposed in t he sam e code m odule. This is t he sim plest archit ect ure t o creat e and probably t he sim plest t o underst andm ost VBA developers will have writ t en an applicat ion of t his t ype and will t herefore underst and, for exam ple, how t he code wit hin t he worksheet code m odule is t riggered. The clearest advant age t his st yle of applicat ion has over all t he ot hers is in t he ease of deploym ent . There is only one filet he workbookt o dist ribut e; t here is no need t o inst all or configure anyt hing; and because t he code is physically st ored wit hin t he workbook, it is im m ediat ely available and working as soon as t he workbook is opened. Unfort unat ely, t he self- aut om at ed workbook's clearest advant age is also it s biggest problem . Because t he code is physically inside t he workbook, how do you issue updat es t o t he code wit hout affect ing t he dat a t hat has been ent ered on t he worksheet s? Alt hough it is possible t o writ e VBA t hat m odifies t he code wit hin anot her workbook, t he user has t o specifically allow t hat t o happen ( in Excel 2002 and above) , and it is only possible t o unprot ect and reprot ect t he VBA proj ect using SendKeys, which cannot be relied on t o work in foreign- language versions of Excel or if Excel does not have t he focus. Even if t he proj ect could be unprot ect ed and reprot ect ed, saving t he updat ed proj ect would rem ove any digit al signat ure t hat had been applied, result ing in m acro virus warnings every t im e t he workbook was subsequent ly opened. The only reliable way self- aut om at ed workbooks can be updat ed is t o provide a com plet ely new workbook wit h a rout ine ( or inst ruct ions) t o copy t he dat a from t he old workbook. Self- aut om at ed workbooks are an ideal choice if t he following condit ions apply: The rout ines cont ained wit hin t he workbook provide specific funct ionalit y for t hat workbook ( as opposed t o general- purpose ut ilit ies) . There will only be one copy of t he workbook ( so any changes can be done direct ly t o t hat workbook) , or The workbook will have a short lifet im e and/ or will be dist ribut ed t o a large ( and m aybe unknown) audience, in which case t he ease of deploym ent becom es a significant considerat ion, and t here is no int ent ion t o dist ribut e any updat es, or The workbook does not cont ain any dat a t hat will need t o be ret ained during an updat e, such as one t hat obt ains it s dat a from an ext ernal dat a source. Codeless and self- aut om at ed workbooks are discussed in m ore det ail in Chapt er 4 Worksheet
Design.
Function and General-Purpose Add-ins An a dd- in is a specific t ype of applicat ion, usually used t o add feat ures t o Excel. The worksheet s in an add- in are hidden from t he user, so t hey never int eract direct ly wit h t he workbook. I nst ead, t he add- in exposes it s rout ines by adding it em s t o Excel's m enus and t oolbars, hooking key com binat ions and/ or exposing funct ions t o be used wit hin a worksheet . The rout ines in an add- in can also be execut ed by t yping t heir fully qualified nam e ( for exam ple, MyAddin.xla! MyRout ine) in t he Tools > Macro > Macros dialog, alt hough t hey do not appear in t he list of available m acros. The rout ines in a general- purpose add- in will always be available t o t he Excel user, so t hey are m ost appropriat e when used for ut ilit y funct ions t hat are designed t o work wit h any file, t ypically using t he ActiveWorkbook, ActiveSheet or Selection obj ect s t o ident ify t he it em s t o operat e on. Care should be t aken t o t idily handle " user error" issues, where t he add- in's rout ines m ay be called from a cont ext in which t hey will not work. For exam ple, if your add- in changes t he case of t he t ext in t he select ed cell, you m ust check t hat t here is a cell select ed ( and not a drawing obj ect , for exam ple) , it isn't locked and it isn't t he result of a form ula. Sim ilarly, if your code applies som e cust om form at t ing t o t he act ive worksheet , you m ust check t hat t here is an act ive sheet ( because t here m ay be no workbooks open) , it is a worksheet ( and not a chart or m acro sheet ) and it is not pr ot ect ed. An add- in is j ust a very hidden workbook, so it doesn't appear in t he list of workbooks or t he VBA Workbooks collect ion, and it s m acros are not list ed in t he Tools > Macro > Macros dialog. I t is, however, j ust like any ot her workbook in alm ost every ot her way and should t herefore be very easy for an int erm ediat e Excel/ VBA developer t o underst and and m aint ain. I ndeed, you can t oggle bet ween having t he workbook behave like an add- in or not by j ust changing t he I sAddin propert y of t he ThisWorkbook obj ect in t he VBE's Propert ies window. Because add- ins never expose t heir worksheet s t o t he user, all t he user int eract ion is done t hrough userform s ( alt hough VBA's InputBox and MsgBox funct ions can be used in sim ple sit uat ions) . This gives us a high level of cont rol over t he user's ent ries, enabling us t o creat e applicat ions t hat are t ot ally robust and reliableassum ing we include dat a- validat ion code and good error handling. I f t he add- in needs t o st ore any inform at ion, such as rem em bering a user's choices, t hat inform at ion should be kept separat e from t he add- in file, by st oring it eit her in t he regist ry ( using SaveSetting/ GetSetting) or in a separat e file ( such as an I NI file) . I f t hat is done, t he add- in will never need t o be saved by t he end user and can be sim ply replaced by a new version if an updat e is required. I f you are willing t o t rust t he end user t o inst all t he add- in correct ly, it is also very easy t o deployj ust send t he XLA file wit h inst ruct ions t o eit her copy it t o t heir Library folder or t o use t he Browse but t on in t he Tools > Add- ins dialog t o locat e t he file. The alt ernat ive is t o use an inst allat ion rout ine t o writ e t he required regist ry ent ries Excel uses t o m aint ain it s add- ins list , such t hat t he add- in is aut om at ically opened and inst alled when t he client next st art s Excel. These regist ry ent ries are covered in det ail in Chapt er 24 Proving Help, Securing, Packaging and Dist ribut ing.
Structure of a Function or General-Purpose Add-in
Most general- purpose add- ins use t he sam e st ruct ure: Code in an Aut o_Open or Workbook_Open rout ine t hat adds t he add- in's m enu it em s and set s up t he keyboard hooks. Each m enu it em has it s OnAct ion propert y set t o call t he appropriat e rout ine in t he add- in file. Separat e rout ines for each m enu it em , locat ed in a st andard m odule. Public funct ions, locat ed in a st andard m odule, exposed for use in worksheet form ulas. Dedicat ed funct ion add- ins oft en cont ain only funct ions and do not add m enu it em s. Code in an Aut o_Close or Workbook_Close rout ine t hat rem oves t he add- in's m enu it em s and clears t he keyboard hooks.
Application-Specific Add-ins As m ent ioned previously, t he m ain problem wit h bot h codeless and self- aut om at ed workbooks is t hat t he " program " is physically st ored in t he sam e file as t he dat a t he end user t ypes in or ot herwise works wit h. I t is very difficult t o reliably updat e t he program part of t hose workbooks wit hout affect ing or in m ost cases dest roying t he end- user's work. The alt ernat ive is t o st ruct ure t he applicat ion such t hat all t he code is cont ained wit hin one workbook, wit h a separat e workbook for t he end user t o use for dat a ent ry, analysis, and so fort h. One such archit ect ure is t hat of an applicat ion- specific add- in. These are very sim ilar t o norm al add- ins, but inst ead of im m ediat ely set t ing up t heir m enu it em s, keyboard hooks, and so on, t hey st ay invisible unt il t he user opens a workbook t he add- in can ident ify as one for which it should m ake it self available. Typically, t he user would be supplied wit h at least t wo workbookst he XLA add- in workbook and a t em plat e workbook t o use for dat a ent ry. The t em plat e workbook( s) will cont ain som e kind of indicat or t he add- in can use t o ident ify it , usually eit her a hidden defined nam e or a cust om docum ent propert y. The key benefit of using an applicat ion- specific add- in is t hat we can safely dist ribut e updat es t o t he code, knowing we will not be dest roying t he user's dat a. There is, however, a sm all price t o pay for t his convenience: Split t ing t he applicat ion int o t wo ( or m ore) workbooks m akes it slight ly harder t o m anage, because we have t o keep t he correct versions of bot h workbooks synchronized during t he developm ent process. Sim ple version cont rol is discussed in m ore det ail in Chapt er 3 Excel and VBA Developm ent Best Pract ices. The applicat ion is slight ly harder for ot her developers t o underst andpart icularly if t hey are used t o single- workbook applicat ions or do not underst and t he t echnique of using class m odules t o hook applicat ion- level event s, as explained in Chapt er 7 Using Class Modules t o Creat e Obj ect s. Deploym ent is m ore com plicat ed because we need t o dist ribut e m ult iple files. Deploym ent st rat egies are discussed in Chapt er 24 Providing Help, Securing, Packaging, and Dist ribut ing.
Structure of an Application-Specific Add-in Applicat ion- specific add- ins are very sim ilar in st ruct ure t o general- purpose add- ins, but wit h ext ra code t o ident ify when t o enable or disable t he m enu it em s: A class m odule used t o t rap t he applicat ion- level event s. Code in an Aut o_Open or Workbook_Open rout ine adds t he add- in's m enu it em s. Each m enu it em has it s OnAct ion propert y set t o call t he appropriat e rout ine in t he add- in file, but t hey are all init ially eit her disabled or hidden. I t t hen creat es an inst ance of t he class m odule and init ializes applicat ion event hooks. Separat e rout ines for each m enu it em , locat ed in a st andard m odule. ( Opt ionally) Public funct ions, locat ed in a st andard m odule, exposed for use in worksheet funct ions. Code in t he class m odule hooks t he applicat ion- level Workbook Act ivat e event , checks whet her t he new workbook has t he correct cust om docum ent propert y, and if so enables t he m enu it em s and set s up t he keyboard hooks. Code in t he class m odule hooks t he applicat ion- level Workbook Deact ivat e event , disables t he m enu it em s, and rem oves t he keyboard hooks. Code in an Aut o_Close or Workbook_Close rout ine rem oves t he add- in's m enu it em s. General- purpose and Applicat ion- specific add- ins are discussed in m ore det ail in Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins.
Dictator Applications All of t he archit ect ures we have considered so far have sought t o enhance Excel in som e way t o im prove t he end user's experience when using our applicat ion. I n cont rast , dict a t or a pplica t ion s seek t o com plet ely t ake over t he Excel user int erface, replacing Excel's m enus wit h t heir own and exercising a very high level of cont rol over t he user. I n t he ideal dict at or applicat ion, users cannot t ell t hey are inside Excel. These applicat ions are creat ed in Excel t o use t he feat ures Excel provides, but t hose feat ures are ent irely cont rolled by t he applicat ion and rarely ( if ever) exposed t o t he user. I nst ead, t he user int erface is m ade up of t ight ly cont rolled dat a- ent ry worksheet s and/ or userform s, designed t o appear like any ot her Windows applicat ion. These applicat ions t ypically require large am ount s of code t o be able t o have t hat level of cont rol, but t hat degree of cont rol enables us t o writ e fullscale, fully funct ional Windows applicat ions, on a par wit h any t hat can be writ t en in Visual Basic or ot her " m ainst ream " applicat ion- developm ent plat form s. I ndeed, by building our applicat ion wit hin Excel, we are im m ediat ely able t o ut ilize t he incredible am ount of funct ionalit y Excel provides. I f t here is a requirem ent for our dict at or applicat ions t o work in Excel 97, we are not able t o have userform s and m enu it em s available at t he sam e t im e, because userform s are always m odal. We t herefore have t he choice t o base our user int erface on using worksheet s and m enus, using userform s wit h but t ons t o navigat e bet ween t he form s or by building m odeless form s using VB6.
Excel 2000 int roduced m odeless userform s, enabling us t o m ake bot h userform s and m enus available and allowing us m uch m ore flexibilit y when deciding whet her t o use a form or worksheet for t he user int erface. For t his reason, we recom m end t hat , if possible, Excel 2000 should be set as t he lowest - level version dict at or applicat ions are designed t o work wit hin. As dict at or applicat ions get m ore and m ore com plex, t hey will oft en st art t o use funct ionalit y t hat only exist s in t he m ost recent versions of Excel ( such as t he XML im port / export int roduced in Excel 2003) , so youas t he designer of t he applicat ionneed t o decide what should happen if t he applicat ion is opened in an older version. I f t he funct ionalit y being used is a core part of t he applicat ion, it is unlikely t he applicat ion will be usable at all in older versions. I f t he use of t he new feat ures can be lim it ed t o a sm all part of t he applicat ion, it m ight m ake m ore sense t o j ust disable t hat m enu it em , or provide separat e rout ines for older versions t o use. Making use of new Excel feat ures will oft en result in com pile errors if t he workbook were t o be opened in an older version, so m any dict at or applicat ions use a " front - loader" workbook t o do an init ial version check, check whet her all ext ernal dependencies ( such as Out look) are available, and t hen open and run t he m ain applicat ion workbook if all t he checks are okay. I f t he checks fail, we can provide m eaningful error m essages t o t he end user ( such as " This applicat ion requires Excel 2000 or above and will not work in Excel 97" ) . There's no escaping t he fact t hat dict at or applicat ions are m uch m ore com plicat ed t han eit her selfaut om at ed workbooks or applicat ion- specific add- ins and will require an int erm ediat e t o advancedlevel Excel/ VBA developer t o creat e and m aint ain t hem . Alt hough t his t ype of archit ect ure can get very com plicat ed, t he com plexit y can be m it igat ed by following t he best - pract ices advice discussed in Chapt er 3 Excel and VBA Developm ent Best Pract ices ( general advice) and Chapt er 6 Dict at or Applicat ions ( specific advice for dict at or applicat ions) . Aft er t he decision t o build a dict at or applicat ion has been m ade, we have an incredible am ount of flexibilit y in t erm s of physically creat ing t he applicat ion. The dat a could be st ored in one or m ore separat e workbooks, local dat abases ( for exam ple, Access dat abases) , or a cent ral dat abase ( for exam ple, SQL Server) . We could decide t o include all t he code in a single workbook or have a sm all " core" add- in, wit h num erous lit t le applet s t hat plug in t o t he core t o provide t he funct ionalit y, where each applet perform s a single, specific t ask. The decision will probably be a t rade- off bet ween ( at least ) t he following considerat ions: A single- workbook st ruct ure is easier for a single developer t o m aint ain, because everyt hing is in t he one place. A m ult iple- workbook st ruct ure is easier for a t eam of developers t o creat e, because each developer can work on his own applet wit hout conflict ing wit h anot her t eam m em ber. I f a m ult iple- workbook st ruct ure is built so each plug- in applet is not loaded unt il it is first used, t he init ial opening of t he core add- in will be quicker t han loading t he full applicat ion of t he single- workbook st ruct urealt hough m odern PCs m ight m ake t hat difference appear im m at er ial. A single- workbook st ruct ure m ust be updat ed in it s ent iret y, but t he applet s of a m ult ipleworkbook st ruct ure can be updat ed and deployed independent ly. The code required t o im plem ent a m ult iple- workbook plug- in archit ect ure is quit e com plex, and m ight be t oo com plex for t he int erm ediat e VBA developer t o fully underst andalt hough we explain it in Chapt er 11 I nt erfaces.
Requirements of a Dictator Application To look and operat e like a st andalone Windows applicat ion, a dict at or applicat ion needs t o m odify m any Application propert ies, from t urning on IgnoreOtherApplications ( so double- clicking an XLS file in Explorer will not use our inst ance of Excel) t o t urning off ShowWindowsInTaskBar in Excel 2000 and above ( because we m ay have m ult iple workbooks t o be m anaged under program cont rol) , as well as hiding all t he com m and bars. Unfort unat ely, Excel will rem em ber m any of t hose set t ings, t o reuse t hem t he next t im e Excel is st art ed, so every dict at or applicat ion m ust st art by recording t he exist ing st at e of all t he set t ings t hat will be changed and rest ore t hem all when it closes. I f t he code t o do t his is writ t en as t wo separat e rout ines and assigned short cut keys, t hey also provide an easy way t o swit ch bet ween t he applicat ion's display and Excel's during developm ent . Aft er a snapshot of t he user's set t ings has been t aken, t he dict at or applicat ion can set t he applicat ion propert ies it requires; it t hen needs t o lock down Excel t o prevent users from doing t hings we do not want t hem t o do, including t he following: Hiding and disabling all t he com m and bars ( including t he short cut com m and bars) , t hen set t ing up our own. Prot ect ing our com m and bars and disabling access t o t he com m and bar cust om izat ion dialog. Disabling all t he short cut key com binat ions t hat Excel provides, and t hen opt ionally reenabling t he few we want t o be exposed t o t he user. Set t ing Application.EnableCancelKey t o xlDisabled at t he st art of every ent ry point , t o prevent users st opping t he code. When using worksheet s as dat a- ent ry form s, we don't want t he user t o be able t o copy and past e ent ire cells, because t hat includes t he form at t ing, dat a validat ion, and so on. Therefore we need t o t urn off drag and drop ( which does a cut and past e) ; t rap bot h Ct rl+ X and Shift + Delet e t o do a copy rat her t han a cut ; and t rap Ct rl+ V, Shift + I nsert , and t he Ent er keys t o ensure we only ever past e values. Having locked down t he Excel environm ent while our applicat ion is running, we need t o provide a m echanism for t he developers t o access t he code, t o enable t hem t o debug t he applicat ion. One m et hod is t o set a global I sDevMode Boolean variable t o True if a part icular file exist s in t he applicat ion direct ory or ( m ore securely) depending on t he Windows usernam e. This Boolean can t hen be used t hroughout t he applicat ion t o provide access point s, such as enabling t he Alt + F11 short cut t o swit ch t o t he VBE, adding a Reset m enu it em and/ or short cut key t o swit ch back t o t he Excel environm ent and not set t ing t he EnableCancelKey propert y, t o allow t he developer t o break int o t he code. The Boolean can also be used wit hin error handlers, t o cont rol whet her t o display a user- or developer- orient ed error m essage.
Structure of a Dictator Application A t ypical dict at or applicat ion uses t he following logical st ruct ure: A front - loader/ st art up rout ine t o perform version and dependency checks and so on
A m ain " core" set of rout ines, t o do t he following: Take a snapshot of t he Excel environm ent set t ings and t o rest ore t hose set t ings Configure and lock down t he Excel applicat ion Creat e and rem ove t he applicat ion's com m and bars Handle copying and past ing dat a wit hin t he worksheet t em plat es Provide a library of com m on helper rout ines and classes ( Opt ionally) I m plem ent a plug- in archit ect ure using class m odules, as described in Chapt er 11 I nt erfaces A backdrop worksheet , t o display wit hin t he Excel window while userform s are being shown, usually wit h som e form of applicat ion- specific logo ( if we are prim arily using form s for t he user int erface) Mult iple independent applet s t hat provide t he applicat ion's funct ionalit y Mult iple t em plat e worksheet s used by t he applet s, such as dat a- ent ry form s or preform at t ed report t em plat es Physically, all t he elem ent s t hat m ake up a t ypical dict at or applicat ion can reside in a single workbook or can be dist ribut ed across m ult iple workbooks. Dict at or applicat ions are discussed in m ore det ail in Chapt er 6 Dict at or Applicat ions.
Technical Implementations I n our discussion of t he m ain t ypes of applicat ion archit ect ure, an underlying assum pt ion is t hat t he applicat ion will be writ t en using VBA. That need not be t he case, however, as we discuss in Chapt ers 19 t hrough 22 , where we exam ine how we can use t he C API t o creat e XLL add- ins and use Visual Basic 6 and/ or VB.Net t o support our VBA rout ines and creat e COM Add- ins. Addit ionally, any of t hese archit ect ures can be im plem ent ed using a t radit ional procedural design ( where m ost of t he funct ionalit y is im plem ent ed using helper rout ines in st andard code m odules) or an obj ect - orient ed approach ( where t he funct ionalit y is im plem ent ed as propert ies and m et hods of class m odules) , as discussed in Chapt er 7 Using Class Modules t o Creat e Obj ect s.
Conclusion The five m ain t ypes of applicat ion archit ect ure have t heir pros and cons, and each is t he m ost applicable for cert ain sit uat ions. The choice of archit ect ure should be t aken carefully, wit h appropriat e considerat ion given t o t he ongoing m aint enance ( probably by a different person t han t he original aut hor) as well as j ust t he ease wit h which t he applicat ion can be creat ed init ially. St yle
Pr os
Co n s
Applica ble For
Codeless
No VBA requirem ent .
Usually clut t ered and hard t o use.
Sim ple dat a- ent ry form s, surveys and so on.
No m acro securit y issues. Easy t o deploy.
Neit her robust nor r eliable. Unable t o provide m uch assist ance t o t he user. Difficult t o updat e.
Selfaut om at ed
Sim ple applicat ion, easy for a beginner VBA developer t o underst and.
I f t he VBA needs t o be updat ed, it will be difficult or im possible t o do so once deployed.
VBA can be used t o im prove robust ness and r eliabilit y .
More com plex dat a- ent ry form s, where t he VBA can be used t o im prove t he qualit y of t he dat a being ent ered, but t here is lit t le dat a st ored in t he workbook long t erm .
Able t o provide lot s of ext ra funct ionalit y for t he user. Easy t o deploy. General addin
Designed t o ext end Excel's Must include robust funct ionalit y. cont ext checks and error handling. Sim ple applicat ion, only Harder t o deploy if it slight ly m ore com plex t han an aut om at ed should be aut om at ically w or k book . ready for use.
I deal for adding ext ra funct ionalit y t o Excel, for use in any workbook.
Easy t o deploy ( alt hough not as sim ple as a w or k book ) . App- specific add- in
Separat es t he code from t he dat a, so t he code can be updat ed wit hout
Slight ly m ore t echnically com plex t han t he general add- in, requires an
This is t he recom m ended st ruct ure t o use for an applicat ion t hat adds t o
St yle
Pr be os updat ed wit hout affect ing t he user's work. Rem oving t he code from t he dat a workbooks m akes t hem sm aller and avoids t he m acro securit y w ar ning.
Dict at or applicat ion
Able t o writ e fully funct ional applicat ions t hat appear t o be applicat ions in t heir own r ight . High degree of cont rol over t he user int eract ion enables us t o writ e very robust and reliable applicat ions. Funct ionalit y can be split over m ult iple workbooks, m aking t hem easier for a t eam t o develop and easier t o deploy updat es.
Co n sin, requires an addint erm ediat e- level VBA dev eloper .
Applica ble t For applicat ion hat adds t o Excel's m enus.
Slight ly harder t o deploy, because it requires at least t wo workbooks t o be inst alled, som et im es t o separat e locat ions. Much m ore com plex t han ot her archit ect ures. Care m ust be t aken t o rest ore t he user's Excel envir onm ent Harder t o deploy, t ypically requiring an inst allat ion r out ine.
The ideal m et hod for a Windows applicat ion t hat m akes heavy use of t he funct ionalit y t hat Excel pr ov ides.
Chapter 3. Excel and VBA Development Best Practices This chapt er appears early in t he book because we want you t o underst and why we are doing cert ain t hings t he way we are in lat er chapt ers. Unfort unat ely, t his also m eans we m ust cover a few t opics in t his chapt er t hat do not get full coverage unt il lat er. For best result s, you m ight want t o review t his chapt er aft er you have read t he rest of t he book. As you read t his chapt er, keep in m ind t hat even t hough t he pract ices described here are generally accept ed best pract ices, t here will always be cert ain cases where t he best t hing t o do is not follow t he best pract ice. We t ry t o point out t he m ost com m on exam ples of t his here and in t he best pract ices discussions in t he chapt ers t hat follow.
Naming Conventions What Is a Naming Convention and Why Is It Important The t erm n a m in g con ve n t ion refers t o t he syst em you use t o nam e t he various part s of your applicat ion. Whenever you declare a variable or creat e a userform , you give it a nam e. You im plicit ly nam e obj ect s even when you do not give t hem a nam e direct ly by accept ing t he default nam e provided when you creat e a userform , for exam ple. One of t he hallm arks of good program m ing pract ice is t he consist ent use of a clearly defined nam ing convent ion for all part s of your VBA applicat ion. Let 's look at an exam ple t hat m ay help dem onst rat e why nam ing convent ions m at t er. I n t he following line of code, what do you know about x?
x = wksDataSheet.Range("A1").Value
From it s usage you can reasonably assum e it is a variable. But what dat a t ype is it designed t o hold? I s it s scope public, m odule level, or privat e? What is it s purpose in t he program ? As it st ands, you cannot answer any of t hese quest ions wit hout spending som e t im e searching t hrough t he rest of t he code. A good nam ing convent ion conveys t he answers t o t hese quest ions wit h a sim ple visual inspect ion of t he variable nam e. Here's a revised exam ple. ( We cover t he specifics in det ail in t he next sect ion.)
glListCount = wksDataSheet.Range("A1").Value
Now you know t he scope of t he variable ( g st ands for global or public scope) , what dat a t ype it was designed t o hold ( l st ands for t he Long dat a t ype) and have a rough idea of t he purpose of t he variable ( it holds t he num ber of it em s in a list ) . A nam ing convent ion helps you t o im m ediat ely recognize t he t ype and purpose of t he building blocks used in an applicat ion. This enables you t o concent rat e on what t he code is doing rat her t han having t o figure out how t he code is st ruct ured. Nam ing convent ions also help m ake your code self- docum ent ing, reducing t he num ber of com m ent s required t o m ake t he purpose of your code clear . We present an exam ple of a well- st ruct ured nam ing convent ion in t he following sect ion. However, t he m ost im port ant t hing about nam ing convent ions is t hat you pick one and use it consist ent ly. As long as everyone involved in a proj ect underst ands t he nam ing convent ion, it doesn't really m at t er exact ly what prefixes you use or how your nam es are capit alized. When it com es t o nam ing
convent ions, consist ency rules, bot h across proj ect s and over t im e.
A Sample Naming Convention A good nam ing convent ion applies not j ust t o variables, but t o all t he elem ent s of your applicat ion. The sam ple nam ing convent ion we present here covers all t he elem ent s in a t ypical Excel applicat ion. We begin wit h a discussion of variables, const ant s and relat ed elem ent s, because t hese are t he m ost com m on elem ent s in any applicat ion. Table 3- 1 shows t he general form at of t he nam ing convent ion. The specific elem ent s of t he nam ing convent ion and t heir purposes are described aft erward.
Ta ble 3 - 1 . A N a m in g Con ve n t ion for Va r ia ble s, Con st a n t s, Use r - D e fin e d Type s a n d En u m e r a t ion s Ele m e n t
N a m in g Con ve n t ion
Var iables
DESCRIPTIVE_NAME
User- defined t ypes Type DESCRIPTIVE_NAME DescriptiveName End Type
Enum er at ions Enum GeneralDescr GeneralDescrSpecificName1 GeneralDescrSpecificName2 End Enum
The Scope Specifier () g Public m Module level ( not hing) Procedure level
The Array Specifier ()
a Array ( not hing) Not an array
The Data Type Specifier () There are so m any dat a t ypes t hat it 's difficult t o provide a com prehensive list of prefixes t o represent t hem . The built - in t ypes are easy. The m ost frequent ly used built - in t ypes get t he short est prefixes. Problem s arise when nam ing obj ect variables t hat refer t o obj ect s from various applicat ions. Som e program m ers use t he prefix obj for all obj ect nam es. This is not accept able. However, devising consist ent , unique and reasonably short prefixes for every obj ect t ype you will ever use is also probably t oo m uch t o ask. Try t o find reasonably m eaningful one- t o t hree- let t er prefixes for t he obj ect variables you use m ost frequent ly and reserve t he obj prefix for obj ect s t hat appear infrequent ly in your code. Make your code clear, and above all, be consist ent . Keep dat a t ype prefixes t o t hree charact ers or fewer. Longer prefixes, in com binat ion wit h scope and array specifiers, m ake for unwieldy variable nam es. Table 3- 2 shows som e suggest ed prefixes for t he m ost com m only used dat a t ypes.
Ta ble 3 - 2 . Su gge st e d N a m in g Con ve n t ion Pr e fix e s Pr e fix
D a t a Type
Pr e fix
D a t a Type
Pr e fix
D a t a Type
b
Boolean
cm
ADODB.Com m and
cbo
MSFor m s.Com boBox [ * ]
byt
Byt e
cn
ADODB.Connect ion
chk
MSForm s.CheckBox
cur
Cur r ency
rs
ADODB.Recordset
cm d
MSFor m s.Com m andBut t on
dt e
Dat e
ddn
MSFor m s.Com boBox [ * * ]
dec
Decim al
cht
Excel.Char t
fra
MSFor m s.Fr am e
d
Double
rng
Excel.Range
lb l
MSFor m s.Label
i
I nt eger
wkb
Ex cel.Wor k book
lst
MSFor m s.List Box
l
Lon g
wks
Excel.Worksheet
m pg
MSFor m s.Mult iPage
ob j
Obj ect
opt
MSFor m s.Opt ionBut t on
sng
Single
cb r
Office.Com m andBar
spn
MSFor m s.SpinBut t on
s
St r ing
ct l
Office.Com m andBar Cont r ol t xt
u
User defined t ype
MSForm s.Text Box
Pr e fix
D a t a Type
Pr e fix
D a t a Type
v
Var iant
cls
User- defined
frm
Pr e fix
D a t a Type
class variable
ref
RefEdit Cont rol
Userform variable
col
VBA.Collect ion
[*] Used for ComboBox controls with a DropDownCombo Style setting. [**] Used for ComboBox controls with a DropDownList Style setting.
Using Descriptive Names VBA gives you up t o 255 charact ers for each of your variable nam es. Use a few of t hem . Don't t ry t o save yourself a lit t le effort by m aking your variable nam es very short . Doing so will m ake your code difficult t o underst and in t he long run, bot h for you and for anyone else who has t o work on it . The Visual Basic I DE provides an aut o- com plet e feat ure for ident ifiers ( all t he nam es used in your applicat ion) . You t ypically need t o t ype only t he first few charact ers t o get t he nam e you want . Ent er t he first few charact ers of t he nam e and press Ct rl+ Spacebar t o act ivat e an aut o- com plet e list of all nam es t hat begin wit h t hose charact ers. As you t ype addit ional charact ers, t he list will cont inue t o narrow down. I n Figure 3- 1 , t he Ct rl+ Spacebar short cut has been used t o display a list of m essage st ring const ant s available t o add t o a m essage box.
Figu r e 3 - 1 . Usin g t h e Ct r l+ Spa ce ba r Sh or t cu t t o Au t o- Com ple t e Lon g Nam es [View full size image]
A Few Words About Enumerations
Enum erat ions are a special t ype of const ant available in Excel 2000 and higher. They enable you t o t ake a list of relat ed values and package t hem up wit h sim ilar, logical friendly nam es. VBA and t he Excel obj ect m odel m ake ext ensive use of enum erat ions. You can see t hese in t he aut o- com plet e list t hat VBA provides for t he values of m any propert ies. For exam ple if you t ype:
Sheet1.PageSetup.PaperSize
=
int o a VBA m odule, you'll be prom pt ed wit h a long list of XlPaperSize enum erat ion m em bers t hat represent t he paper sizes available t o print on. Figure 3- 2 shows t his in act ion.
Figu r e 3 - 2 . Th e Ex ce l Pa pe r Size En u m e r a t ion M e m be r List [View full size image]
These nam es act ually represent num eric const ant s whose values you can exam ine if you look t hem up in t he Obj ect Browser, discussed in Chapt er 16 VBA Debugging. Not ice t he st ruct ure of t hese enum erat ion nam es. First , t hey all begin wit h a prefix ident ifying t he applicat ion t hey are associat ed wit h, in t his case xl, which obviously st ands for Excel. Second, t he first part of t heir nam e is a descript ive t erm t hat t ies t hem t oget her visually as belonging t o t he sam e enum erat ed t ype, in t his case Paper. The last part of each enum erat ion nam e is a unique st ring describing t he specific value. For exam ple, xlPaper11x17 represent s 11x17 paper and xlPaperA4 represent s A4 paper. This syst em for nam ing enum erat ed const ant s is very com m on and is t he one we use in t his book .
Naming Convention Examples Nam ing convent ion descript ions in t he abst ract are difficult t o connect t o real- world nam es, so we show som e real- world exam ples of our nam ing convent ion in t his sect ion. All of t hese exam ples are t aken direct ly from com m ercial- qualit y applicat ions writ t en by t he aut hors.
Variables gsErrMsg A public variable wit h t he dat a t ype St ring used t o st ore an error m essage mauSettings() A m odule- level array of user- defined t ype t hat holds a list of set t ings cbrMenu A local variable wit h t he dat a t ype Com m andBar t hat holds a reference t o a m enu b ar
Constants gbDEBUG_MODE A public const ant of t ype Boolean t hat indicat es whet her t he proj ect is in debug m ode msCAPTION_FILE_OPEN A m odule- level const ant of dat a t ype St ring t hat holds t he capt ion for a user- defined file open dialog (Application.GetOpenFilename in t his inst ance) lOFFSET_START A local const ant of dat a t ype Long holding t he point at which we begin offset t ing from som e Range obj ect
User-Defined Types The following is a public user- defined t ype t hat is used t o st ore t he dim ensions and locat ion of an obj ect . I t consist s of four variables of dat a t ype Double t hat st ore t he t op, left , widt h and height of t he obj ect and a variable of dat a t ype Boolean used t o indicat e whet her t he set t ings have been saved.
Public Type DIMENSION_SETTINGS bSettingsSaved As Boolean dValTop As Double dValLeft As Double dValHeight As Double dValWidth As Double End Type
The variables wit hin a user- defined t ype definit ion are called m e m be r va r ia ble s. These can be declared in any order. However, our nam ing convent ion suggest s you sort t hem alphabet ically by dat a t ype unless t here is a st rong reason t o group t hem in som e ot her fashion.
Enumerations
The following is a m odule- level enum erat ion used t o describe various t ypes of days. The sch prefix in t he nam e of t he enum erat ion st ands for t he applicat ion nam e. This enum erat ion happens t o com e from an applicat ion called Scheduler. DayType in t he enum erat ion nam e indicat es t he purpose of t he enum erat ion and each of t he individual enum erat ion m em bers has a unique suffix t hat describes what it m eans.
Private Enum schDayType schDayTypeUnscheduled schDayTypeProduction schDayTypeDownTime schDayTypeHoliday End Enum
I f you don't indicat e what values you want t o give your enum erat ion m em bers, VBA aut om at ically assigns a value of zero t o t he first m em ber in t he list and increm ent s t hat value by one for each addit ional m em ber. You can easily override t his behavior and assign a different st art ing point from which VBA will begin increm ent ing. For exam ple, t o m ake t he enum erat ion above begin wit h one inst ead of zero, you would do t he following:
Private Enum schDayType schDayTypeUnscheduled = 1 schDayTypeProduction schDayTypeDownTime schDayTypeHoliday End Enum
VBA will cont inue t o increm ent by one for each m em ber aft er t he last m em ber for which you've specified a value. You can override aut om at ic assignm ent of values t o all of your enum erat ion m em bers by sim ply specifying values for all of t hem . Figure 3- 3 shows one of t he prim ary advant ages of using enum erat ions. VBA provides you wit h an aut o- com plet e list of pot ent ial values for any variable declared as a specific enum erat ion.
Figu r e 3 - 3 . Eve n Cu st om En u m e r a t ion s Ge t a VBA Au t o- Com ple t e List in g
Procedures Subrout ines and funct ions are grouped under t he m ore general t erm pr ocedur e. Always give your procedures very descript ive nam es. Once again, you are allowed up t o 255 charact ers for your procedure nam es, and procedure nam es appear in t he Ct rl+ Spacebar aut o- com plet e list , so don't sacrifice a nam e t hat m akes t he purpose of a procedure obvious for one t hat 's sim ply short . I t is not a com m on pract ice t o do so, but we find t hat giving funct ions a prefix indicat ing t he dat a t ype of t heir ret urn value t o be very helpful in underst anding code. When calling a funct ion, always place open and closed parent hesis aft er t he funct ion nam e t o dist inguish it from a variable or subrout ine nam e, even if t he funct ion t akes no argum ent s. List ing 3- 1 shows a well- nam ed Boolean funct ion being used as t he t est for an If...Then st at em ent .
List in g 3 - 1 . An Ex a m ple of N a m in g Con ve n t ion s for Fu n ct ion N a m e s If bValidatePath("C:\Files") Then ' The If...Then block is executed ' if the specified path exists. End If
Subrout ines should be given a nam e t hat describes t he t ask t hey perform . For exam ple, a subrout ine nam ed Shut downApplicat ion leaves lit t le doubt as t o what it does. Funct ions should be given a nam e t hat describes t he value t hey ret urn. A funct ion nam ed sGet UnusedFilenam e( ) can reasonably be expect ed t o ret urn a filenam e.
The nam ing convent ion applied t o procedure argum ent s is exact ly t he sam e as t he nam ing convent ion for procedure- level variables. For exam ple, t he bValidat ePat h funct ion shown in List ing 3 - 1 would be declared in t he following m anner:
Function bValidatePath(ByVal sPath As String) As Boolean
Modules, Classes and Userforms I n our sam ple nam ing convent ion, t he nam es of st andard code m odules should be prefixed wit h an uppercase M, class m odules wit h an uppercase C and userform s wit h an upper case F. This has t he advant age of neat ly sort ing t hese obj ect s in t he VBE Proj ect window if you don't care for t he folder view, as shown in Figure 3- 4 .
Figu r e 3 - 4 . Cla ss M odu le s, Use r for m s a n d St a n da r d M odu le s Sor t e d in t h e Pr oj e ct W in dow
This convent ion also m akes code t hat uses classes and userform obj ect s m uch clearer. I n t he following code sam ple, for exam ple, t his nam ing convent ion m akes it very clear t hat you are
declaring an obj ect variable of a cert ain class t ype and t hen creat ing a new inst ance of t hat class:
Dim clsMyClass As CMyClass Set clsMyClass = New CMyClass
I n each case, t he nam e on t he left is a class va r ia ble , and t he obj ect on t he right is a cla ss.
Worksheets and Chart Sheets Because t he CodeNam es of worksheet s and chart sheet s in your proj ect are t reat ed by VBA as int rinsic obj ect variables t hat reference t hose sheet s, t he CodeNam es given t o worksheet s and chart sheet s should follow variable nam ing convent ions. Worksheet CodeNam es are prefixed wit h wks t o ident ify t hem in code as references t o Worksheet obj ect s. Sim ilarly, chart sheet s are prefixed wit h cht t o ident ify t hem as references t o Excel Chart obj ect s. For bot h t ypes of sheet s, t he prefix should be followed by a descript ive t erm indicat ing t he sheet 's purpose in t he applicat ion. Figure 3- 4 , for exam ple, shows a wksCom m andBars worksheet t hat cont ains a t able defining t he com m and bars creat ed by t he applicat ion. For sheet s cont ained wit hin an add- in or hidden in a workbook and not designed t o be seen by t he user, t he sheet t ab nam e should be ident ical t o t he CodeNam e. For sheet s t hat are visible t o t he user, t he sheet t ab nam e should be a friendly nam e, and one t hat you should be prepared for t he user t o change. As discussed lat er, you should always rely on sheet CodeNam es rat her t han sheet t ab nam es wit hin your VBA code.
The Visual Basic Project Not ice in Figure 3- 4 t hat t he Visual Basic Proj ect has been given t he sam e nam e as t he workbook it 's associat ed wit h. You should always give your VBProj ect a nam e t hat clearly ident ifies t he applicat ion it belongs t o. There's not hing worse t han having a group of workbooks open in t he VBE wit h all of t hem having t he sam e default nam e VBAProj ect . I f you plan on creat ing references bet ween proj ect s, you will be required t o give t hem unique nam es.
Excel UI Naming Conventions Excel user int erface elem ent s used in t he creat ion of an applicat ion should also be nam ed using a consist ent and well- defined nam ing convent ion. We covered worksheet s and chart sheet s in a previous sect ion. The t hree ot her m aj or cat egories of Excel UI elem ent s t hat can be nam ed are shapes, em bedded obj ect s and defined nam es.
Shapes The t erm Sh a pe s refers t o t he generic collect ion t hat can cont ain t he wide variet y of obj ect s you
can place on t op of a worksheet or chart sheet . Shapes can be broadly divided int o t hree cat egories: cont rols, drawing obj ect s and em bedded obj ect s. Shapes should be nam ed sim ilarly t o obj ect variables, which is t o say t hey should be given a prefix t hat ident ifies what t ype of obj ect t hey are followed by a descript ive nam e indicat ing what purpose t hey serve in t he applicat ion. Many cont rols t hat can be placed on userform s can be placed on worksheet s as well. Worksheet s can also host t he old Form s t oolbar cont rols, which are sim ilar t o t he Act iveX MSForm s cont rols but wit h t heir own unique advant ages and disadvant ages. Chapt er 4 Worksheet Design discusses t hese in m ore det ail. Cont rols placed on worksheet s should be nam ed using exact ly t he sam e convent ions you would use for cont rols placed on userform s. Worksheet s can also host a wide variet y of drawing obj ect s ( t echnically known as Shapes) t hat are not st rict ly cont rols, alt hough you can assign m acros t o all of t hem . These fall int o t he sam e nam ing convent ion cat egory as t he wide variet y of obj ect s t hat you can use in VBA. I t would be very difficult t o devise unique prefixes for all of t hem , so use well- defined prefixes for t he m ost com m on drawing obj ect s and use a generic prefix for t he rest . Here are som e sam ple prefixes for t hree of t he m ost com m only used drawing obj ect s: p ic
Pict ure
rec
Rect angle
t xt
Text Box ( not t he Act iveX cont r ol)
Embedded Objects The t erm e m be dde d obj e ct is used here t o refer t o Excel obj ect s such as Pivot Tables, QueryTables and Chart Obj ect s, as well as obj ect s creat ed by applicat ions ot her t han Excel. Worksheet s can host a variet y of em bedded obj ect s. Com m on exam ples of non- Excel em bedded obj ect s would include equat ions creat ed wit h t he Equat ion Edit or and WordArt drawings. Sam ple prefixes for em bedded obj ect s are shown here: cht
Char t Obj ect
eqn Equat ion q r y Quer y Table pvt
Piv ot Table
ar t
Wor dAr t
Defined Names Our nam ing convent ion for defined nam es is a bit different t han for ot her program elem ent s. I n t he case of defined nam es, t he prefix should indicat e t he broad purpose of t he defined nam e, as opposed t o t he dat a t ype it 's expect ed t o hold. This is because nont rivial Excel applicat ions t ypically have m any defined nam es t hat are m uch easier t o work wit h if t hey are grouped t oget her by purpose wit hin t he Define Nam e dialog. When a worksheet cont ains dozens or hundreds of defined
nam es, t here are significant efficiencies t o be gained by having nam es wit h relat ed funct ions grouped t oget her by prefix in t he defined nam e list . The descript ive nam e port ion of a defined nam e is used t o specify exact ly what purpose t he nam e serves wit hin it s broader cat egory. The following list shows som e exam ples of purpose prefixes for defined nam es: cht
Chart dat a r ange
con
Nam ed const ant
err
Error check
for
Nam ed for m ula
inp
I nput range
out
Out put range
pt r
Specific cell locat ion
rgn
Region
set
UI set t ing
t bl
Table
ExceptionsWhen Not to Apply the Naming Convention You want t o break t he general rule and not apply your nam ing convent ion in t wo specific sit uat ions. The first is when you are dealing wit h elem ent s relat ed t o Windows API calls. These elem ent s have been nam ed by Microsoft , and t he nam es are well known wit hin t he program m ing com m unit y. The Windows API const ant s, user- defined t ypes, procedure declarat ions and procedure argum ent s should appear in your code exact ly as t hey appear in t he Microsoft Plat form SDK, which can be viewed on t he MSDN Web sit e at : ht t p: / / m sdn.m icr osoft .com / libr ar y / en- us/ w inpr og/ w inpr og/ w indow s_api_st ar t _page.asp Not e t hat t his reference is provided in C/ C+ + form at only. The second sit uat ion where you want t o avoid applying your own nam ing convent ions is when you
Best Practices for Application Structure and Organization Application Structure The One-Workbook vs. the N-Workbook Application The num ber of workbooks used in an Excel applicat ion is driven prim arily by t wo fact ors: t he com plexit y of t he applicat ion it self and t he lim it at ions im posed by applicat ion dist ribut ion issues. Sim ple applicat ions and t hose for which you cannot im pose a form al inst allat ion sequence dem and t he fewest num ber of workbooks. Com plex applicat ions and t hose over which you have com plet e cont rol of t he inst allat ion process allow division int o m ult iple workbooks or ot her file t ypes such as DLLs. Chapt er 2 Applicat ion Archit ect ures discusses t he various t ypes of Excel applicat ions and t he st ruct ure suit ed t o each. When you have t he libert y t o divide your applicat ion across m ult iple files, t here are a num ber of good reasons t o do so. These include separat ion of t he logical t iers in your applicat ion, separat ion of code from dat a, separat ion of user- int erface elem ent s from code elem ent s, encapsulat ing funct ional elem ent s of t he applicat ion and m anaging change conflict s in a t eam developm ent envir onm ent .
Separation of Logical Tiers Alm ost every nont rivial Excel applicat ion has t hree dist inct logical t iers or sect ions: Th e u se r - in t e r fa ce t ie r The user- int erface t ier consist s of all t he code and visible elem ent s required for your applicat ion t o int eract wit h t he user. I n an Excel applicat ion, t he userint erface t ier consist s of visible elem ent s such as worksheet s, chart s, com m and bars, userform s and t he code required t o direct ly m anage t hose visible elem ent s. The user- int erface t ier is t he only logical t ier t hat cont ains elem ent s visible t o t he user. Th e bu sin e ss logic or a pplica t ion t ie r The business logic t ier is com plet ely code based. I t s code perform s t he core operat ions t he applicat ion was designed t o accom plish. The business logic t ier accept s input from t he user- int erface t ier and ret urns out put t o t he user- int erface t ier. For long- running operat ions, t he business logic t ier m ay t ransm it periodic updat es t o t he user- int erface t ier in t he form of st at us bar m essages or progress bar updat es. Th e da t a a cce ss a n d st or a ge t ie r The dat a access and st orage t ier is responsible for t he st orage and ret rieval of dat a required by t he applicat ion. This can be as sim ple as reading from and writ ing dat a t o cells on a local, hidden worksheet or as com plex as execut ing st ored procedures in a SQL Server dat abase across a net work. The dat a access and st orage t ier com m unicat es direct ly only wit h t he business logic t ier.
As Figure 3- 5 shows, all t hree t iers are necessary for a com plet e applicat ion, but t hey m ust not be inext ricably linked. The t hree t iers of your applicat ion should be loosely coupled, such t hat a significant change in one t ier does not require significant changes t o t he ot her t wo. St rongly coupled applicat ion t iers inevit ably lead t o m aint enance and upgrade difficult ies.
Figu r e 3 - 5 . Th e Re la t ion sh ips Am on g t h e Th r e e Tie r s of a n Ex ce l Applica t ion
For exam ple, if your dat a access and st orage t ier needs t o m ove from using an Access dat abase for st orage t o using a SQL Server dat abase for st orage, you want t he changes required t o be isolat ed wit hin t he dat a access and st orage t ier. I n a well- designed applicat ion, neit her of t he ot her t wo t iers would be affect ed in any way by such a change. I deally, dat a should be t ransferred bet ween t he business logic t ier and t he dat a access and st orage t ier in t he form of user- defined t ypes. These provide t he best t rade- off bet ween efficiency and loose coupling. Alt ernat ively, ADO Recordset obj ect s can be used, but t hese int roduce subt le linkage issues t hat it would be bet t er if t he business logic layer didn't rely on, such as t he order of fields ret urned from t he dat abase. Sim ilarly, if you need t o provide an alt ernat e Web- based present at ion int erface for your applicat ion, loose coupling bet ween t he user- int erface t ier and t he business logic t ier will m ake it m uch easier t o accom plish. This is because t here will be no im plicit assum pt ions built in t o t he business logic t ier regarding how t he user int erface is const ruct ed. Elem ent s t hat accept dat a input from t he user should be com plet ely self- cont ained. The business logic t ier should pass t he userint erface t ier t he dat a it requires for init ializat ion as sim ple dat a t ype propert ies. The user- int erface t ier should collect t he user input and pass it back t o t he business logic t ier as sim ple dat a t ype propert ies or as a UDT for m ore com plex int erfaces. Because t he business logic t ier should have no int rinsic knowledge of how t he user- int erface is const ruct ed, referencing cont rols on a userform direct ly from a business logic t ier procedure is expressly forbidden.
Separation of Data/UI from Code Wit hin t he user- int erface t ier of m any Excel applicat ions lie t wo unique subt iers. These consist of t he workbook and sheet elem ent s used t o const ruct t he user- int erface and t he code support ing t hose elem ent s. The concept of separat ion should be applied rigorously t o t hese subt iers. A workbook- based int erface should cont ain no code, and t he UI code t hat cont rols a workbook- based int erface should reside in an add- in com plet ely separat ed from t he workbook it cont rols. The reasoning for t his separat ion is t he sam e as t he reasoning described above for separat ing t he m ain applicat ion t iers, isolat ing t he effect s of change. Of all t he applicat ion t iers, t he user- int erface t ier undergoes t he m ost frequent changes. Therefore it 's not sufficient j ust t o isolat e user int erface
changes t o t he user int erface t ier; you should also isolat e changes t o t he visible elem ent s of t he user int erface from t he code t hat cont rols t he user int erface. We provide real- world exam ples of applicat ion t ier separat ion in t he chapt ers t hat follow, so don't be concerned if what is discussed here is not t ot ally obvious t o you at t his point .
Application Organization for Procedural Programming Procedural program m ing is t he program m ing m et hodology m ost developers are fam iliar wit h. I t involves dividing an applicat ion int o m ult iple procedures, each of which is designed t o perform a specific t ask wit hin t he applicat ion. An ent ire applicat ion can be writ t en in procedural fashion, procedural elem ent s can be com bined wit h obj ect - orient ed elem ent s or an ent ire applicat ion can be writ t en in obj ect - orient ed fashion. This sect ion focuses on best pract ices for procedural program m ing. We discuss obj ect - orient ed program m ing t echniques in Chapt er 7 Using Class Modules t o Creat e Obj ect s.
Organizing Code into Modules by Function/Category The prim ary purpose of separat ing code int o m odules is t o im prove t he com prehensibilit y and m aint ainabilit y of t he applicat ion. I n a procedural applicat ion, procedures should be organized int o separat e code m odules in a logical fashion. The best way t o do t his is t o group procedures t hat perform sim ilar funct ions int o t he sam e code m odule.
TI P VBA has an undocum ent ed " soft lim it " on t he m axim um size of any single st andard code m odule. A st andard code m odule should not exceed 64KB as m easured by it s t ext file size when export ed from t he proj ect . ( The VBETools ut ilit y included on t he CD will report m odule sizes for you aut om at ically.) Your proj ect will not crash im m ediat ely upon a single m odule exceeding t his 64KB lim it , but consist ent ly exceeding t his lim it will alm ost invariably lead t o an unst able applicat ion.
Functional Decomposition Funct ional decom posit ion refers t o t he process of breaking your applicat ion int o separat e procedures such t hat each procedure is responsible for a single t ask. I n t heory, you could writ e m any applicat ions as one huge, m onolit hic procedure. However, doing so would m ake your applicat ion ext rem ely difficult t o debug and m aint ain. By using funct ional decom posit ion, you design your applicat ion such t hat it consist s of m ult iple procedures t hat are each responsible for a well- defined t ask t hat is easy t o underst and, validat e, docum ent and m aint ain.
Best Practices for Creating Procedures
A com prehensive set of guidelines for creat ing good procedures could easily fill a chapt er of it s own. We cover t he m ost im port ant guidelines in t he following list : En ca psu la t ion Whenever possible, a procedure should be designed t o com plet ely encapsulat e t he logical operat ion it perform s. I deally, your procedures should have no linkages t o anyt hing out side of t hem . This m eans, for exam ple, t hat a properly encapsulat ed procedure can be copied int o a com plet ely different proj ect and work j ust as well t here as it did in t he proj ect where it originat ed. Encapsulat ion prom ot es code reuse and sim plifies debugging by isolat ing different logical operat ions from each ot her. Elim in a t ion of du plica t e code When writ ing a nont rivial Excel applicat ion, you will frequent ly discover you are writ ing code t o perform t he sam e operat ion in m ult iple places. When t his occurs, you should fact or t his duplicat ed code out int o a separat e procedure. Doing so reduces t he num ber of places where t hat part icular operat ion needs t o be validat ed or m odified t o one. The com m on procedure can also be opt im ized in one place, and t he benefit s will be felt t hroughout your applicat ion. All of t his leads t o a significant im provem ent in code qualit y. I t also serves a second im port ant purpose, m aking your code m ore reusable. As you fact or com m on operat ions int o dedicat ed procedures, you will discover t hat you can oft en reuse t hese procedures in ot her applicat ions. This t ype of code form s t he basis of a code library t hat you can use t o increase your product ivit y when writ ing new applicat ions. The m ore logical operat ions you have available as com plet e, fully t est ed library procedures, t he less t im e it will t ake for you t o develop a new applicat ion. I sola t ion of com ple x ope r a t ion s I n m any real- world applicat ions, you will find t hat som e sect ions of t he business logic are bot h com plex and very specific t o t he applicat ion for which t hey were designed ( t hat is, not reusable) . These sect ions of business logic should be isolat ed int o separat e procedures for ease of debugging and m aint enance. Pr oce du r e size r e du ct ion Procedures t hat are overly long are difficult t o underst and, debug and m aint ain, even for t he program m er who wrot e t hem . I f you discover a procedure cont aining m ore t han 150 t o 200 lines of code, it is probably t rying t o accom plish m ult iple goals and t herefore should be fact ored int o m ult iple single- purpose procedures. Lim it in g t h e n u m be r of pr oce du r e a r gu m e n t s The m ore argum ent s a procedure accept s, t he m ore difficult it will be t o underst and and t he less efficient it will be t o execut e. I n general, you should lim it t he num ber of procedure argum ent s t o five or fewer. And don't sim ply replace procedure argum ent s wit h public or m odule- level variables. I f you find yourself requiring m ore t han five procedure argum ent s, it 's probably a good sign t hat your procedure, or your applicat ion logic, needs t o be redesigned.
General Application Development Best Practices This sect ion covers best developm ent pract ices com m on t o all applicat ion developm ent areas. Most of t he ot her chapt ers in t his book recom m end furt her best pract ices relat ed specifically t o t he subj ect of t hat chapt er.
Code Commenting Good code com m ent ing is one of t he m ost im port ant pract ices in Excel applicat ion developm ent . Your code com m ent s should provide a clear and com plet e descript ion of how your code is organized, how each obj ect and procedure should be used and what you are t rying t o accom plish wit h your code. Com m ent s also provide a m eans of t racking changes t o your code over t im e, a subj ect we cover lat er in t his chapt er. Code com m ent s are im port ant t o bot h you and t o ot her developers who m ay need t o work on your code. The ut ilit y of code com m ent s t o ot her developers should be self- evident . What you m ight not realize unt il t he cruel fist of experience has pounded it int o you is t hat your com m ent s are very im port ant t o you as well. I t is very com m on for a developer t o writ e an init ial version of an applicat ion and t hen be asked t o revise it subst ant ially aft er a long period of t im e has passed. You would be surprised at how foreign even your own code looks t o you aft er it has been out of sight and out of m ind for a long period of t im e. Code com m ent s help solve t his problem . Com m ent s should be applied at all t hree m aj or levels of your applicat ion's code: t he m odule level, t he procedure level and t o individual sect ions or lines of code. We discuss t he t ypes of com m ent ing appropriat e t o each of t hese levels below.
Module-Level Comments I f you have used t he m odule nam ing convent ions described previously in t his chapt er, t hen anyone exam ining your code will have a rough idea of t he purpose of t he code cont ained wit hin each m odule. You should supplem ent t his wit h a brief com m ent at t he t op of each m odule t hat provides a m ore det ailed descript ion of t he purpose of t he m odule.
N OTE For t he purposes of code com m ent ing, when we use t he t erm m odule, we m ean it t o include st andard m odules, class m odules and t he code m odules behind userform s.
A good m odule- level com m ent should be locat ed at t he very t op of t he m odule and look som et hing
like t he exam ple shown in List ing 3- 2 .
List in g 3 - 2 . A Sa m ple M odu le - Le ve l Com m e n t ' ' Description: ' ' Option Explicit
A brief description of the purpose of the code in this module.
Procedure-Level Comments Procedure- level com m ent s are t ypically t he m ost det ailed com m ent s in your applicat ion. I n a procedure- level com m ent block, you describe t he purpose of t he procedure, usage not es, a det ailed list of argum ent s and t heir purposes and a descript ion of expect ed ret urn values in t he case of funct ions. Procedure- level com m ent s can also serve a rudim ent ary change- t racking purpose by providing a place t o add dat es and descript ions of changes m ade t o t he procedure. A good procedure- level com m ent such as t he one shown in List ing 3- 3 would be placed direct ly above t he first line of t he procedure. The procedure- level com m ent in List ing 3- 3 is designed for a funct ion. The only difference bet ween a com m ent block for a funct ion and a com m ent block for a subrout ine is t he subrout ine com m ent block does not cont ain a Ret urns sect ion, obviously because subrout ines do not ret urn a value.
List in g 3 - 3 . A Sa m ple Pr oce du r e - Le ve l Com m e n t '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Comments: Locates the chart to be operated on or asks ' the user to select a chart if multiple charts ' are located. ' ' Arguments: chtChart Returned by this function. An ' object reference to the chart to ' be operated on, or Nothing on user ' cancel. ' ' Returns: Boolean True on success, False on error ' or user cancel. ' ' Date Developer Action ' ------------------------------------------------------------' 07/04/02 Rob Bovey Created ' 10/14/03 Rob Bovey Error trap for charts with no
' 11/18/03 '
Rob Bovey
series Error trap for no active workbook
Internal Comments I nt ernal com m ent s are com m ent s t hat appear wit hin t he body of t he code it self. These com m ent s should be used t o describe t he purpose of any code where t he purpose is not self- evident . I nt ernal com m ent s should describe t he in t e n t of t he code rat her t han t he ope r a t ion of t he code. The dist inct ion bet ween int ent and operat ion is not always clear, so List ing 3- 4 and List ing 3- 5 show t wo exam ples of t he sam e code, one wit h a bad com m ent and t he ot her wit h a good com m ent .
List in g 3 - 4 . Ex a m ple of a Ba d I n t e r n a l Code Com m e n t ' Loop the asInputFiles array. For lIndex = LBound(asInputFiles) To UBound(asInputFiles) '... Next lIndex
The com m ent in List ing 3- 4 is m onum ent ally unhelpful. First of all, it describes only t he line of code direct ly below it , giving you no clue about t he purpose of t he loop st ruct ure as a whole. Second, t he com m ent is sim ply an exact writ t en descript ion of t hat line of code. This inform at ion is easy enough t o det erm ine by j ust looking at t he line of code. I f you rem oved t he com m ent shown in List ing 3- 4 , you would not lose any inform at ion at all.
List in g 3 - 5 . Ex a m ple of a Good I n t e r n a l Code Com m e n t ' Import the specified list of input files into the working area ' of our data sheet. For lIndex = LBound(asInputFiles) To UBound(asInputFiles) '... Next lIndex
I n List ing 3- 5 , we have a com m ent t hat adds value t o t he code. Not only does it describe t he int ent , rat her t han t he operat ion of t he code, it also explains t he ent ire loop st ruct ure. Aft er reading t his com m ent , you know what you're looking at as you delve int o t he code wit hin t he loop. As wit h m ost rules, t here are except ions t o t he int ernal com m ent guidelines specified above. The m ost im port ant except ion concerns com m ent s used t o clarify cont rol st ruct ures. If...Then st at em ent s and Do... Loops can m ake code difficult t o underst and as t hey becom e wider, because
you can no longer see t he ent ire cont rol st ruct ure in a single code window. At t hat point , it becom es difficult t o rem em ber what t he applicable cont rol expression was. For exam ple, when evaluat ing a lengt hy procedure we have oft en found ourselves looking at som et hing like t he code snippet shown in List ing 3- 6 .
List in g 3 - 6 . I n scr u t a ble Con t r ol St r u ct u r e s End If lNumInputFiles = lNumInputFiles - 1 Loop End If
I n List ing 3- 6 , what are t he logical t est s being m ade by t he t wo If...Then st at em ent s, and what expression cont rols t he Do...While loop? Aft er t hese st ruct ures have been filled wit h a subst ant ial am ount of code, you sim ply cannot t ell wit hout scrolling back and fort h wit hin t he procedure, because t he ent ire block is no longer visible wit hin a single code window. You can alleviat e t his problem very easily by using t he end- of- cont rol- block com m ent ing st yle shown in List ing 3- 7 .
List in g 3 - 7 . Un de r st a n da ble Con t r ol St r u ct u r e s End If
' If bContentsValid Then
lNumInputFiles = lNumInputFiles - 1 Loop End If
' Do While lNumInputFiles > 0 ' If bInputFilesFound Then
The com m ent s in List ing 3- 7 , alt hough t hey j ust rest at e t he code at t he t op of each cont rol st ruct ure, m ake it com plet ely obvious what you are looking at . These t ypes of com m ent s should be used anywhere you have a cont rol st ruct ure wit hin your code t hat is t oo large t o fit com plet ely int o one code window.
Avoiding the Worst Code-Commenting Mistake I t m ight seem obvious, but t he m ost frequent and dam aging m ist ake relat ed t o code com m ent ing is not keeping t he com m ent s updat ed as you m odify t he code. We have frequent ly seen proj ect s t hat appeared at first glance t o im plem ent good code- com m ent ing pract ices, but upon closer
exam inat ion discovered t he com m ent s were creat ed for som e ancient version of t he proj ect and now bore alm ost no relat ionship t o t he current code. When at t em pt ing t o underst and a proj ect , bad com m ent s are worse t han no com m ent s at all. Bad com m ent s are act ively m isleading. Always keep your com m ent s current . Old com m ent s can eit her be delet ed or ret ained as a series of change- t racking records. We recom m end rem oving obsolet e in- line com m ent s or t hey will quickly clut t er your code, m aking it difficult t o underst and sim ply due t o t he num ber of lines of inapplicable com m ent s t hat accum ulat e. Use procedure- level com m ent s as a change- t racking m echanism where necessary.
Code Readability Code readabilit y is a funct ion of how your code is physically arranged. Good visual layout of code enables you t o infer a significant am ount of inform at ion about t he logical st ruct ure of t he program . This is a key point . Code layout m akes not one bit of difference t o t he com put er. I t s sole purpose is t o assist hum ans in underst anding t he code. Like nam ing convent ions, t he consist ent use of good code layout convent ions m akes your code self- docum ent ing. The prim ary t ool of code layout is whit e space. Whit e space includes space charact ers, t abs and blank lines. The following paragraphs discuss t he m ost im port ant ways t o use whit e space t o produce a well- designed code layout . Group relat ed code elem ent s t oget her and separat e unrelat ed code elem ent s wit h blank lines. Sect ions of code separat ed by blank lines wit hin a procedure can be t hought of as serving a sim ilar funct ion t o paragraphs wit hin t he chapt ers of a book. They help you det erm ine what t hings belong t oget her. List ing 3- 8 shows an exam ple of how blank lines can im prove code readabilit y. Even wit hout t he code com m ent s, it would be obvious which lines of code are relat ed.
List in g 3 - 8 . Usin g Bla n k Lin e s t o Gr ou p Re la t e d Se ct ion s of Code ' Reset Application properties. Application.ScreenUpdating = True Application.DisplayAlerts = True Application.EnableEvents = True Application.StatusBar = False Application.Caption = Empty Application.EnableCancelKey = xlInterrupt Application.Cursor = xlDefault ' Delete all custom CommandBars For Each cbrBar In Application.CommandBars If Not cbrBar.BuiltIn Then cbrBar.Delete Else cbrBar.Enabled = True End If Next cbrBar ' Reset the Worksheet Menu bar. With Application.CommandBars(1)
.Reset .Enabled = True .Visible = True End With
Wit hin a relat ed sect ion of code, alignm ent is used t o indicat e which lines of code belong t oget her. I ndent at ion is used t o show t he logical st ruct ure of t he code. List ing 3- 9 shows a single sect ion fr om List ing 3- 8 where alignm ent and indent at ion have been used t o good effect . You can look at t his sect ion of code and underst and im m ediat ely which elem ent s go t oget her as well as deduce t he logical flow of t he code's execut ion.
List in g 3 - 9 . Pr ope r Use of Align m e n t a n d I n de n t a t ion ' Delete all custom CommandBars For Each cbrBar In Application.CommandBars If Not cbrBar.BuiltIn Then cbrBar.Delete Else cbrBar.Enabled = True End If Next cbrBar
Line cont inuat ion can be used t o m ake com plex expressions and long declarat ions m ore readable. Keep in m ind t hat breaking code int o cont inued lines solely for t he purpose of m aking t he ent ire line visible wit hout scrolling is not necessarily a good pract ice and can oft en m ake code m ore confusing. List ing 3- 10 shows exam ples of j udicious use of line cont inuat ion.
List in g 3 - 1 0 . Ju diciou s Use of Lin e Con t in u a t ion ' Complex expressions are easier to understand ' when properly continued If (uData.lMaxLocationLevel > 1) Or _ uData.bHasClientSubsets Or _ (uData.uDemandType = bcDemandTypeCalculate) Then End If ' Line continuations make long API declarations easier to read. Declare Function SHGetSpecialFolderPath Lib "Shell32.dll" _ (ByVal hwndOwner As Long, _ ByRef szBuffer As String, _ ByVal lFolder As Long, _ ByVal bCreate As Long) As Long
VBA Programming Best Practices General VBA Best Practices
Use of Module Directives Option Explicit Always use t he Option Explicit st at em ent in every m odule. The im port ance of t his pract ice cannot be overst at ed. Wit hout Option Explicit, any t ypographical error you m ake result s in VBA aut om at ically creat ing a new Variant variable. This t ype of error is very insidious because it m ay not even cause an im m ediat e runt im e error. However, it will cert ainly cause your applicat ion t o event ually ret urn incorrect result s. This t ype of bug m ay very well pass wit hout not ice unt il your applicat ion is dist ribut ed, and it will be difficult t o debug under any circum st ances. The Option Explicit st at em ent forces you t o explicit ly declare all t he variables you use. Option Explicit causes VBA t o t hrow a com pile- t im e error ( init iat ed by select ing Debug > Com pile from t he VBE m enu) whenever an unrecognized ident ifier nam e is encount ered. This m akes it very easy t o discover and correct t ypographical errors. You can ensure t hat Option Explicit is aut om at ically placed at t he t op of every m odule you creat e by choosing Tools > Opt ions > Edit or from t he VBE m enu and checking t he Require Variable Declarat ion check box. This set t ing is st rongly recom m ended. Option Private Module The Option Private Module st at em ent m akes all procedures wit hin t he m odule where it is used unavailable from t he Excel user- int erface or from ot her Excel proj ect s. Use t his st at em ent t o hide procedures t hat should not be called from out side your applicat ion.
TI P The Application.Run m et hod can circum vent t he Option Private Module st at em ent and run privat e procedures in m odules where t his st at em ent has been used.
Option Base 1 The Option Base 1 st at em ent causes all array variables whose lower bound has not been specified t o have a lower bound of 1. Do not use t he Option Base 1 st at em ent . I nst ead, always specify bot h t he upper and lower bounds of every array variable you use. A procedure creat ed in a m odule t hat uses Option Base 1 m ay m alfunct ion if copied t o a m odule in which t his st at em ent isn't used. This behavior inhibit s one of t he m ost im port ant procedure design goals, t hat of reusabilit y. Option Compare Text The Option Compare Text st at em ent forces all st ring com parisons
wit hin t he m odule where it is used t o be t ext based rat her t han binary. I n a t ext - based st ring com parison, upper- and lowercase versions of t he sam e charact er are t reat ed as ident ical, whereas in a binary com parison t hey are different . The Option Compare Text st at em ent should be avoided for t he sam e reason Option Base 1 should be avoided. I t m akes procedures behave different ly when placed in m odules wit h t he st at em ent versus m odules wit hout it . Text - based com parisons are also m uch m ore com put at ionally expensive t han binary com parisons, so Option Compare Text slows down all st ring com parison operat ions in t he m odule where it 's locat ed. Most Excel and VBA st ring com parison funct ions provide an argum ent you can use t o specify binary or t ext - based com parison. I t 's m uch bet t er t o use t hese argum ent s t o provide t ext - based com parisons only where you need t hem . There are som e rare cases where Option Compare Text is required. The m ost frequent case occurs when you need t o do non- case- sensit ive st ring com parisons wit h t he VBA Like operat or. The only way t o get t he Like operat or t o perform in a non- case- sensit ive m anner is t o use t he Option Compare Text st at em ent . I n t his case, you should isolat e t he procedures t hat require t his st at em ent in a separat e code m odule so ot her procedures t hat don't require t his opt ion aren't adversely affect ed. Be sure t o docum ent why you have done t his in a m odule- level com m ent .
Variables and Constants
Avoid Reusing Variables Each variable declared in your program should serve one purpose only. Using t he sam e variable for m ult iple purposes saves you only one variable declarat ion line but int roduces m assive pot ent ial for confusion wit hin your program . I f you are t rying t o det erm ine how a procedure works and you have figured out what a cert ain variable does in a cert ain place, you will nat urally assum e t he variable serves t he sam e purpose t he next t im e you see it . I f t his is not t he case, t he code logic will becom e very difficult t o underst and.
Avoid the Variant Data Type Avoid t he use of t he Variant dat a t ype whenever possible. Unfort unat ely, VBA is not a st rongly t yped program m ing language. Therefore, you can sim ply declare variables wit hout specifying t heir dat a t ype and VBA will creat e t hese variables as Variant s. The m ain reasons not t o use Variant s are as follows: Va r ia n t s a r e ve r y in e fficie n t This is because int ernally, a Variant is a very com plex st ruct ure designed t o hold any dat a t ype in t he VBA program m ing language. Variant values cannot be accessed and m odified direct ly as can fundam ent al dat a t ypes such as Long and Double. I nst ead, VBA m ust use a series of com plex Windows API calls behind t he scenes whenever it needs t o perform any operat ion on a Variant . D a t a st or e d in a va r ia n t ca n be h a ve u n e x pe ct e dly Because Variant s are designed t o hold any t ype of dat a, t he dat a t ype t hat goes int o a Variant is not necessarily t he dat a t ype t hat will com e out of it . When accessing t he dat a in a Variant , VBA will at t em pt t o coerce t he dat a int o what ever dat a t ype it t hinks m akes t he m ost sense in t he cont ext of t he operat ion. I f you
m ust use Variant s, always explicit ly cast t hem t o t he dat a t ype you want when using t heir values.
Beware of Evil Type Coercion Evil Type Coercion ( ETC) is anot her sym pt om t hat result s from VBA not being a st rongly t yped program m ing language. ETC occurs when VBA aut om at ically convert s one dat a t ype t o anot her com plet ely unrelat ed dat a t ype. The m ost frequent exam ples are St rings t hat hold num bers being convert ed t o I nt egers and Booleans being convert ed t o t heir St ring equivalent s. Don't m ix variables of different dat a t ypes in your code wit hout using t he explicit cast ing funct ions ( CStr, CLng, CDbl and so on) t o t ell VBA exact ly how you want t hose variables t o be t reat ed.
Avoid the As New Declaration Syntax Never declare obj ect variables using t he As New synt ax. For exam ple, t he following form of an obj ect variable declarat ion should never be used:
Dim rsData As New ADODB.Recordset
I f VBA encount ers a line of code t hat uses t his variable and t he variable has not been init ialized, VBA will aut om at ically creat e a new inst ance of t he variable. This is n e v e r t he behavior you want . Good program m ing pract ice im plies t hat t he program m er should m aint ain com plet e cont rol over t he creat ion of all t he obj ect s used in t he program . I f VBA encount ers an uninit ialized obj ect variable in your code, it is alm ost cert ainly t he result of a bug, and you want t o be not ified about it im m ediat ely. Therefore, t he proper way t o declare and init ialize t he obj ect variable shown above is as follows:
Dim rsData As ADODB.Recordset Set rsData = New ADODB.Recordset
Using t his st yle of declarat ion and init ializat ion, if t he obj ect variable is dest royed som ewhere in your procedure and you inadvert ent ly reference it again aft er t hat point , VBA will im m ediat ely t hrow t he runt im e error " Obj ect variable or Wit h block variable not set ," not ifying you of t he pr oblem .
Always Fully Qualify Object Names Always use fully qualified obj ect nam es in variable declarat ions and code wit h t heir class nam e prefix. The reason for t his is because m any obj ect libraries share t he sam e obj ect nam es. I f you j ust declare a variable wit h an obj ect nam e alone and t here are m ult iple obj ect libraries wit h t hat obj ect nam e being referenced by your applicat ion, VBA will creat e a variable from t he first library in
t he Tools > References list where it finds t he obj ect nam e you used. This is oft en not what you want . UserForm cont rols present t he m ost com m on sit uat ion where problem s result from obj ect variable declarat ions t hat aren't fully qualified. For exam ple, if you want ed t o declare an obj ect variable t o reference a Text Box cont rol on your userform , you m ight be inclined t o do t he following:
Dim txtBox As TextBox Set txtBox = Me.TextBox1
Unfort unat ely, as soon as VBA at t em pt s t o execut e t he second line of code, a " Type m ism at ch" error would be generat ed. This is because t he Excel obj ect library cont ains a Text Box obj ect and t he Excel obj ect library com es before t he MSForm s obj ect library in t he Tools > References list . The correct way t o writ e t his code is shown here:
Dim txtBox As MSForms.TextBox Set txtBox = Me.TextBox1
Never Hard Code Array Bounds When you are looping t he cont ent s of an array variable, never hard- code t he array bounds in loop. Use t he LBound and UBound funct ions inst ead, as shown in List ing 3- 11.
List in g 3 - 1 1 . Th e Cor r e ct W a y t o Loop a n Ar r a y Dim lIndex As Long Dim alListItems(1 To 10) As Long ' Load the array here. For lIndex = LBound(alListItems) To UBound(alListItems) ' Do something with each value. Next lIndex
The reason for t his is because array bounds frequent ly change over t he course of creat ing and m aint aining an applicat ion. I f you hard- code t he array bounds 1 and 10 in t he loop shown above, you will have t o rem em ber t o updat e t he loop any t im e t he bounds of t he alListItems array change. Failure t o do so is a frequent source of errors. By using LBound and Ubound, you m ake t he loop self- adj ust ing.
Always Specify the Loop Counter After a Next Statement List ing 3- 11 dem onst rat es anot her good coding pract ice. You should always specify t he loop count er variable aft er a Next st at em ent . Even t hough t his is not st rict ly required by VBA, doing so m akes your code m uch easier t o underst and, especially if t he dist ance bet ween t he For and Next st at em ent s is long.
Make Use of Constants Const ant s are very useful program m ing elem ent s. They serve t he following purposes in your code, am ong ot hers: Const ant s elim inat e " m agic num bers," replacing t hem wit h recognizable nam es. For exam ple, in t he following line of code, what does t he num ber 50 m ean? If lIndex < 50 Then
There is no way of knowing unless you wrot e t he code and you st ill rem em ber what 50 represent s. I f inst ead you saw t he following, you would have a very good idea of what t he If...Then t est was looking for:
Const lMAX_NUM_INPUT_FILES As Long = 50 ' More code here. If lIndex < lMAX_NUM_INPUT_FILES Then
I f you need t o know t he value of a const ant at design t im e, you can j ust right - click over t he const ant nam e in t he VBE and choose Definit ion from t he short cut m enu. You will be brought direct ly t o t he line where t he const ant is defined. I n break m ode at runt im e it 's even easier. Just hover your m ouse over t he const ant and a t oolt ip window cont aining it s value will appear . Const ant s im prove coding efficiency and avoid errors by elim inat ing duplicat e dat a. I n t he preceding exam ple, assum e you reference t he m axim um num ber of input files in several places t hroughout your program . At som e point you m ay need t o upgrade your program t o handle m ore files. I f you have hard- coded t he m axim um num ber of input files everywhere you've needed t o use it , you will have t o locat e all of t hese places and change t he num ber in each one. I f you've used a const ant , all you need t o do is m odify t he value of t he single const ant declarat ion and t he new value will aut om at ically be used wherever t he const ant has been used in your code. This sit uat ion is a very frequent source of errors t hat can be elim inat ed by sim ply using const ant s inst ead of hard- coded num bers.
Variable Scope Public variables are dangerous. They can be m odified anywhere in your applicat ion wit hout warning, m aking t heir values unpredict able. They also work against one of t he m ost im port ant program m ing precept s: encapsulat ion. Always creat e variables wit h t he m inim um scope possible. Begin by creat ing all of your variables wit h local ( procedure- level) scope and only widen t he scope of a variable when it is absolut ely necessary. As wit h m ost of our ot her rules, t here are a few cases where t he use of public variables is useful and/ or necessary. When dat a m ust be passed deep int o t he st ack before it is used. For exam ple, if procedure A reads som e dat a, t hen passes t hat dat a t o procedure B, which passes it t o procedure C, which passes it t o procedure D where t he dat a is finally used, a good case can be m ade t hat t he dat a should be passed direct ly from procedure A t o procedure D by way of a public variable. Cert ain inherent ly public classes, such as an applicat ion- level event handling class, require a public obj ect variable so t hey never go out of scope while your applicat ion is running.
Early Binding vs. Late Binding The dist inct ion bet ween early binding and lat e binding is widely m isunderst ood and oft en confused wit h how an obj ect is creat ed. The on ly t hing t hat affect s whet her an obj ect is early bound or lat e bound is how t he obj ect variable holding t he reference t o t he obj ect was declared. Variables declared as a specific obj ect dat a t ype are always early bound. Variables declared wit h t he Obj ect or Variant dat a t ype are always lat e bound. List ing 3- 12 shows an exam ple of a lat e bound reference, and List ing 3- 13 shows an exam ple of an early bound reference.
List in g 3 - 1 2 . A La t e Bou n d Re fe r e n ce t o a n AD O Con n e ct ion Obj e ct Dim objConnection As Object ' It doesn't matter ' late bound due to Set objConnection = Set objConnection =
how you create the object, it's still the As Object variable declaration. New ADODB.Connection CreateObject("ADODB.Connection")
List in g 3 - 1 3 . An Ea r ly Bou n d Re fe r e n ce t o a n AD O Con n e ct ion Obj e ct Dim cnConnection As ADODB.Connection ' It doesn't matter how you create the object, it's still early ' bound due to the data type used in the variable declaration.
Set cnConnection = New ADODB.Connection Set cnConnection = CreateObject("ADODB.Connection")
Not e t hat t o use early binding wit h obj ect s t hat are out side t he Excel obj ect m odel you m ust set a reference t o t he appropriat e obj ect library using t he Tools > References m enu in t he Visual Basic Edit or. For exam ple, t o creat e early bound variables referencing ADO obj ect s, you m ust set a reference t o t he Microsoft Act iveX Dat a Obj ect s 2.x Library, where x is t he version of ADO you int end t o use. You should use early bound obj ect variables wherever possible. Early bound obj ect variables provide t he following advant ages over lat e bound variables: I m pr ove d pe r for m a n ce When you use an obj ect variable whose dat a t ype is known t o VBA at com pile t im e, VBA can look up t he m em ory locat ions of all propert y and m et hod calls you use wit h t his obj ect and st ore t hem wit h your code. At runt im e, when VBA encount ers one of t hese early bound propert y or m et hod calls, it sim ply execut es t he code locat ed at t he st ored locat ion. ( This is a bit of an oversim plificat ion. What VBA act ually st ores is a num eric offset t o t he code t o be execut ed from a known st art ing point in m em ory, which is t he beginning of a st ruct ure called t he obj ect 's VTable.) When you use a lat e bound obj ect variable, VBA has no way of knowing in advance what t ype of obj ect t he variable will cont ain. Therefore, it cannot opt im ize any propert y or m et hod calls at com pile t im e. This m eans t hat each t im e VBA encount ers a lat e bound propert y or m et hod call at runt im e, it m ust query t he variable t o det erm ine what kind of obj ect it holds, look up t he nam e of t he propert y or m et hod being execut ed t o det erm ine where in m em ory it is locat ed and t hen execut e t he code locat ed at t hat m em ory address. This process is significant ly slower t han an early bound call. St r ict t ype ch e ck in g I n t he lat e bound exam ple in List ing 3- 12, if you accident ally set your obj ect variable t o reference an ADO Com m and obj ect inst ead of a Connect ion obj ect , VBA would not com plain. You would only discover you had a problem downst ream in your code when you t ried t o use a m et hod or propert y not support ed by t he Com m and obj ect . Wit h early binding, VBA will im m ediat ely det ect t hat you are t rying t o assign t he wrong t ype of obj ect reference t o your obj ect variable and not ify you wit h a " Type m ism at ch" error. I ncorrect propert y and m et hod calls can be det ect ed even earlier, before t he code is ever run. VBA will at t em pt t o look up t he nam e of t he propert y or m et hod being called from wit hin t he appropriat e obj ect library at com pile t im e and t hrow an error if t he nam e cannot be locat ed. I n t e lliSe n se a va ila bilit y Early bound obj ect variables m ake for m uch easier program m ing as well. Because VBA knows exact ly what t ype of obj ect a variable represent s, it can parse t he appropriat e obj ect library and provide a drop- down list of all available propert ies and m et hods for t he obj ect as soon as you t ype a dot operat or aft er t he variable's nam e. As you m ight expect , in som e cases you need t o use lat e binding rat her t han early binding. The t wo m ost com m on reasons for using lat e binding rat her t han early binding are as follows:
1 . When a newer version of an applicat ion's obj ect library has broken com pat ibilit y wit h an earlier version.
This is an all t oo com m on sit uat ion. I f you set a reference t o t he lat er version of t he applicat ion's obj ect library in your applicat ion and t hen at t em pt t o run it on a com put er t hat has t he earlier version, you will get an im m ediat e com pile t im e error " Can't find proj ect or library," and t he reference on t he t arget m achine will be prefixed wit h MI SSI NG. The m ost insidious t hing about t his error is t hat t he line of code flagged as being t he source of t he error will oft en have not hing t o do wit h t he obj ect library act ually causing t he problem . I f you need t o use obj ect s from an applicat ion t hat exhibit s t his problem and you want t o support users wit h any version of t he applicat ion, you need t o use lat e binding for all variables referencing obj ect s from t he applicat ion. I f you are creat ing new obj ect s, you also need t o use t he Creat eObj ect funct ion wit h t he version- independent ProgI D of t he obj ect you want t o creat e, rat her t han t he = New ObjectName synt ax. 2 . When you want t o use an applicat ion t hat you cannot be sure will exist on t he user's com put er and t hat you cannot inst all yourself. I n t his case, you need t o use lat e binding t o avoid t he com pile t im e error t hat would im m ediat ely result from at t em pt ing t o run an applicat ion t hat referenced an obj ect library t hat did not exist on t he user's com put er. Your applicat ion can t hen check for t he exist ence of t he obj ect library in quest ion and exit gracefully if t hat library is not inst alled on t he user's com put er .
TI P Even if you will event ually use lat e binding in your code, early binding offers such a great increase in product ivit y while coding t hat you should writ e and t est t he applicat ion using early binding. Convert your code t o lat e binding only for t he final round of t est ing and dist r ibut ion.
Defensive Coding Defensive coding refers t o various program m ing pract ices designed t o help you prevent errors rat her t han having t o correct t hem aft er t hey occur.
Write Your Application in the Earliest Version of Excel That You Expect It to Run In Alt hough t he Microsoft Excel t eam has done a bet t er j ob t han m ost of m aint aining backward com pat ibilit y wit h earlier versions of Excel, t here are m any subt le differences bet ween t he versions. I f you are very fam iliar wit h a lat er version of Excel, you can easily writ e an applicat ion t hat will not run on an earlier version because som e feat ure you used did not exist in t hat version. The solut ion t o t his problem is t o always develop your applicat ions in t he earliest version of Excel t hat you expect t hem t o run in. This m ay force you t o m aint ain m ult iple versions of Excel on one com put er, or bet t er yet , separat e com put ers for each version of Excel. Eit her way, t his is an essent ial pract ice. I f you develop an applicat ion in Excel 2000, give it t o a user in Excel 97 and find
out it doesn't run, you will need t o debug and rem ove any code t hat doesn't work in Excel 97. You will save m uch t im e and st ress by sim ply developing t he applicat ion using Excel 97 t o begin wit h.
Explicitly Use ByRef or ByVal I f a procedure t akes argum ent s, t here are t wo ways t o declare t hose argum ent s: ByRef or ByVal. ByRef This convent ion m eans you are passing t he m em ory address of t he variable rat her t han t he value of t he variable. I f t he called procedure m odifies a ByRef argum ent , t he m odificat ion will be visible in t he calling procedure. ByVal This convent ion m eans you are passing a value t o t he procedure. A procedure can m ake changes t o a ByVal argum ent , but t hese changes will not be visible t o t he calling procedure. I n fact , a procedure can use ByVal argum ent s exact ly as if t hey were locally declared variables. Always explicit ly declare your procedure argum ent s as ByRef or ByVal. I f you do not specify t his, all argum ent s are creat ed ByRef by default . You should declare procedure argum ent s ByVal unless you have a specific need for t he calling procedure t o see changes m ade t o t he argum ent s. Declaring ar gum ent s ByVal will prevent changes m ade t o t hose argum ent s from being propagat ed back t o t he calling procedure. The only except ions are when you are passing large st rings ( very large st rings) , which are far m ore efficient ly passed ByRef, or when your procedure argum ent is of a t ype, such as an array, t hat cannot be passed ByVal. Be aware t hat declaring procedure argum ent s ByVal does leave you m ore exposed t o Evil Type Coercion. A ByRef procedure argum ent m u st be passed t he sam e dat a t ype as it is declared t o accept ; ot herwise a com pile t im e error will result . By cont rast , VBA will at t em pt t o coerce a value passed t o a ByVal procedure argum ent int o a com pat ible dat a t ype.
Explicitly Call the Default Property of an Object Wit h t he possible except ion of t he I t em propert y of a Collect ion obj ect , it 's never a good idea t o im plicit ly invoke t he default propert y of an obj ect j ust by using t he obj ect 's nam e in an expression. List ing 3- 14 shows t he right way and t he wrong way of accessing t he default propert y of an obj ect using an MSForms.TextBox cont rol for dem onst rat ion purposes ( t he Text propert y is t he default propert y of an MSForms.TextBox cont rol) .
List in g 3 - 1 4 . D e fa u lt Pr ope r t ie s ' The right way. txtUsername.Text = "My Name" ' The wrong way txtUsername = "My Name"
By avoiding t he im plicit use of default propert ies, you m ake your code m uch m ore readable and prot ect yourself from errors if t he default behavior of t he obj ect changes in som e fut ure version of Excel or VBA.
Validate Arguments Before Using Them in Procedures I f your procedure accept s input argum ent s t hat m ust have cert ain propert ies in order t o be validfor exam ple, if t hey m ust be wit hin a specific range of valuesverify t hat t he values passed t o t hose argum ent s are valid before at t em pt ing t o use t hem in your procedure. The idea is t o cat ch erroneous input as soon as possible so t hat you can generat e a m eaningful error m essage and sim plify your debugging. Wherever possible, creat e t est harnesses t o validat e t he behavior of your procedures. A t est harness is a wrapper procedure t hat can call t he procedure being t est ed m ult iple t im es, passing it a wide range of argum ent s, and t est t he result t o be sure it is correct . Chapt er 16 VBA Debugging discusses t est harnesses in det ail.
Use Guard Counters to Protect Against Infinite Loops Program your loops t o aut om at ically handle infinit e loop condit ions. One of t he m ost com m on m ist akes m ade when using Do...While or While...Wend loops is t o creat e a sit uat ion where t he loop cont rol condit ion is never sat isfied. This causes t he loop t o run forever ( or unt il you can force your code t o break by pressing Ct rl+ Break if you are lucky, or by using t he Windows Task Manager t o shut down your applicat ion if you are not ) . Always add a count er t hat aut om at ically bails out when t he num ber of loops execut ed is known t o be m ore t han t he highest num ber t hat should ever occur in pract ice. List ing 3- 15 shows a Do...While loop wit h an infinit e loop guard st ruct ure.
List in g 3 - 1 5 . Gu a r d Aga in st I n fin it e Loops Dim bContinueLoop As Boolean Dim lCount As Long bContinueLoop = True lCount = 1 Do ' The code that goes here should set the ' bContinueLoop variable to False once the ' loop has achieved its purpose. ' This infinite loop guard exits the loop ' unconditionally after 10000 iterations. lCount = lCount + 1 If lCount > 10000 Then Exit Do
Loop While bContinueLoop
The only purpose of t he lCount variable wit hin t he loop is t o force t he loop t o exit if t he code wit hin t he loop fails t o set t he cont rol variable t o exit wit hin 10,000 it erat ions. ( The appropriat e num ber would depend on t he part icular sit uat ion.) This t ype of const ruct adds very lit t le overhead t o your loop; if perform ance is a significant concern, however, use t he infinit e loop guard unt il you are sure all t he code wit hin t he loop is funct ioning properly, and t hen delet e it or com m ent it out .
Use Debug > Compile Early and Often Never let your code st ray m ore t han a few changes away from being able t o run a flawless Debug > Com pile. Failing t o adhere t o t his pract ice will lead t o long, inefficient debugging sessions.
Use CodeNames to Reference Sheet Objects Always reference worksheet s and chart sheet s in your applicat ion by t heir CodeNam e. Depending on sheet t ab nam es t o ident ify sheet s is risky because you or your users m ay change t hese t ab nam es, breaking any code t hat uses t hem .
Validate the Data Types of Selections I f you writ e a procedure designed t o operat e on a specific t ype of obj ect t he user has select ed, always check t he obj ect t ype of t he select ion using eit her t he TypeName funct ion or t he If TypeOf...Is const ruct . For exam ple, if you need t o operat e on a range select ed by t he user, ensure t hat t he select ion really is a Range obj ect before cont inuing, as shown in List ing 3- 16.
List in g 3 - 1 6 . Ve r ify Th a t t h e Se le ct ion I s t h e Cor r e ct Obj e ct Type ' Code designed to operate on a range. If TypeOf Selection Is Excel.Range Then ' OK, it's a Range object. ' Continue code execution. Else ' Error, it's not a Range object. MsgBox "Please select a range.", vbCritical, "Error!" End If
Change Control Change cont rol, also known as version cont rol, at t he m ost basic level involves t wo pract ices:
m aint aining a set of prior versions of your applicat ion t hat you can use t o recover from various program m ing or t echnical errors and docum ent ing changes m ade t o your applicat ion over t im e.
Saving Versions When m ost professional program m ers t alk about version cont rol, t hey m ean t he use of dedicat ed version cont rol soft ware, such as Microsoft Visual Source Safe. However, t his t ype of soft ware is expensive, has a st eep learning curve and doesn't int egrat e well wit h applicat ions built in Excel. This is because Excel doesn't st ore it s m odules nat ively as separat e t ext files. The version cont rol m et hod we will suggest here is quick, sim ple, requires no special soft ware and delivers t he m ost crucial benefit s of a t radit ional version cont rol syst em . The m ost im port ant obj ect ive of a version cont rol syst em is t o enable you t o recover an earlier version of your proj ect if you have encount ered som e significant problem wit h t he version you are current ly working on. I f a significant code m odificat ion has gone t erribly wrong or you suddenly find yourself wit h a corrupt file, you will be in a very difficult posit ion if you do not have a recent backup t o help you recover. A sim ple version cont rol syst em t hat can save you from t hese problem s would be im plem ent ed in a fashion sim ilar t o t he following. First creat e a folder nam ed Backup as a subfolder t o t he folder in which your proj ect is st ored. Each t im e you prepare t o m ake a significant addit ion or m odificat ion t o your proj ect , or at m inim um once a day, use a file- com pression ut ilit y such as WinZip t o zip all t he files in your proj ect folder int o a file wit h t he following nam e form at : Backup_YYYYMMDDHH.zip, where Y st ands for year, M st ands for m ont h, D st ands for day and H st ands for hour. This nam ing form at will give your backup file a unique nam e t hat will sort in correct sequent ial order when viewed in Windows Explorer. Move t his file int o your Backup folder and cont inue working. I f you encount er a problem , you can recover your proj ect from t he m ost recent backup. You will obviously lose som e work, but if you save backup versions diligent ly you can m inim ize t he loss. Each t im e you are sure you have a fully t est ed build of your proj ect , you can delet e m ost of t he int erm ediat e files from your Backup folder. I t is advisable t o ret ain at least weekly backups t hroughout t he life of a proj ect .
Documenting Changes with Comments When you are m aint aining code, if you m ake a significant change t o t he logic of a procedure you should also m ake a not e wit h a brief descript ion of t he change, t he dat e it was m ade and your nam e in t he procedure- level com m ent block ( see List ing 3- 3 ) . All nont rivial m odificat ions t o your code should be not ed wit h an int ernal com m ent t hat includes t he dat e t he change was m ade and t he nam e of t he developer who m ade t he change if t here are m ult iple developers working on t he applicat ion.
Conclusion Whet her you use t he nam ing convent ion proposed here or creat e your own, use a nam ing convent ion consist ent ly across all your applicat ions and over t im e. I t will m ake your code selfdocum ent ing and easy t o follow. Code t he separat e logical t iers of your applicat ion as independent ent it ies. This will prevent changes in one logical t ier from forcing you t o rebuild m uch of your applicat ion. Com m ent your code liberally at all levels. When t rying t o underst and t he purpose of a sect ion of code, it 's a lot easier if t hat purpose is explained by a code com m ent t han if you have t o figure it out yourself. Following t hese and all t he ot her best pract ices present ed in t his chapt er will result in robust , underst andable and m aint ainable applicat ions.
Chapter 4. Worksheet Design There is a t rem endous am ount of Excel user int erface design t hat can and should be accom plished using t he built - in feat ures of Excel alone, wit h no VBA required. One of t he guiding principles of Excel developm ent is " let Excel be Excel." Don't t ry t o reinvent t he wheel. Excel provides a wide variet y of prepackaged, perform ance- opt im ized feat ures you can use t o build your applicat ion's user int erface. This chapt er exam ines how you can produce a fully funct ional user int erface wit h j ust t he feat ures Excel provides for t his purpose. There are t wo fundam ent al sect ions of an Excel worksheet user int erface: t hose designed t o be visible t o t he user and t hrough which t he user operat es your applicat ion, and t hose designed t o be hidden from t he user and used only by your applicat ion t o perform t he t asks required of it . We cover each of t hese sect ions in m ore det ail in t his chapt er.
Principles of Good Worksheet UI Design The following list provides som e design guidelines t hat apply t o all worksheet user int erfaces:
1 . Use form at t ing t o creat e visual cont rast bet ween cells designed t o serve different purposesinput cells versus form ula cells, for exam pleas well as visual separat ion bet ween different sect ions of your user int erface. 2 . Use consist ent form at t ing based on t he purpose of each cell. For exam ple, don't form at input cells wit h a whit e background in one area and a green background in anot her. 3 . Don't use garish colors. Your choice of form at t ing should not dist ract from t he t ask at hand. 4 . Creat e a logical, well- st ruct ured flow t hrough your user int erface. Your user int erface should flow from left t o right t hen t op t o bot t om wit hin a worksheet and from left t o right am ong m ult iple worksheet s. 5 . Make it obvious t o users what t hey are supposed t o do each t im e t hey are required t o perform som e act ion. Techniques for doing t his include t he use of cell com m ent s, validat ion list s, default values, good descript ive field nam es and so on. 6 . Use dynam ic input - verificat ion t echniques t o provide feedback as quickly as possible if t he user has done som et hing wrong. Wait ing unt il t he user has com plet ed t he ent ire form t o point out dat a- ent ry errors should be viewed as a last resort , t o be used only when t here are no good alt ernat ives. 7 . Don't creat e an environm ent t hat pot ent ially allows t he user t o m ake cat ast rophic m ist akes. Prot ect all of your user int erface worksheet s, leaving only cells t hat require dat a ent ry unlocked. This prevent s crit ical form ulas from being accident ally overwrit t en. 8 . Don't allow t he user t o get lost . Rest rict t he area of t he worksheet wit hin which t he user can navigat e t o t he working area of your user int erface.
Program Rows and Columns: The Fundamental UI Design Technique When you design a user int erface on an Excel worksheet , one of t he first t hings you should do is leave row 1 and colum n A em pt y. This sect ion of t he worksheet will be hidden from t he user and will allow your applicat ion t o perform m any t asks associat ed wit h an advanced Excel UI , including error checking, st oring validat ion list s and calculat ing int erm ediat e values. I n very com plex worksheet user int erfaces, it is not uncom m on t o have several init ial rows and/ or colum ns used as hidden work areas. These are called pr ogr a m r ow s and pr ogr a m colu m n s. An Excel worksheet user int erface is t ypically laid out in a t able form at , left t o right , t op t o bot t om . I m plem ent ing design principle 6 described above is m ost easily accom plished if you have a hidden area you can use t o aut om at ically exam ine each of t he user's ent ries and det erm ine whet her t hey m eet all t he crit eria t hat are enforceable using worksheet - based const ruct s. The result of t hese t est s can t hen be used by condit ional form at t ing and/ or VBA- based validat ion t o signal users when t hey have ent ered dat a incorrect ly. I n t he sim ple t im esheet exam ple shown in Figure 4- 1 , t he user com plet es t he first t hree colum ns of t he t able. The last colum n of t he t able is calculat ed by t he worksheet . The first colum n of t he worksheet it self is designed t o be a hidden colum n. I t perform s a sim ple validat ion check on each row of t he t im esheet t able. I t count s t he num ber of ent ries m ade by t he user in each row and ret urns True if t he num ber of ent ries is incorrect ( which is t o say t he user has not com plet ed all of t he required ent ries for t hat row) .
Figu r e 4 - 1 . An Ex a m ple of H idde n Colu m n D a t a Va lida t ion [View full size image]
Here t here are only t wo possible valid condit ions. Eit her a row has not yet been used and t herefore has zero ent ries, or a row has been com plet ely filled out , in which case t here will be t hree ent ries. Any ot her condit ion is an error. Not ice t hat t he error- checking form ula for row 6 indicat es t here is a
dat a- ent ry error in t hat row. This is because t he user has not yet ent ered a St op Tim e. The user m ay very well elim inat e t his error by ent ering a St op Tim e aft er he com plet es t his t ask. I f he doesn't , it is a sim ple m at t er for your applicat ion t o exam ine t he validat ion range in colum n A and det erm ine t here is an error.
Defined Names Defined nam es are an int egral part of worksheet user int erface design. Defined nam es are a superset of t he m ore com m only underst ood nam ed range feat ure. Defined nam es include nam ed const ant s, nam ed ranges and nam ed form ulas. Each t ype of defined nam e serves an im port ant purpose and all nont rivial Excel worksheet user int erfaces use som e or all of t he defined nam e t ypes. The nam ing convent ions used for t he defined nam es dem onst rat ed in t his chapt er are described in Chapt er 3 Excel and VBA Developm ent Best Pract ices.
Named Constants A defined nam e can refer t o a const ant value. For exam ple, t he set HiddenCols defined const ant shown in Figure 4- 2 refers t o t he value 1.
Figu r e 4 - 2 . A Sa m ple N a m e d Con st a n t
This nam e illust rat es a t ypical use of defined const ant s: st oring set t ings t hat will be m ade t o a user int erface worksheet . I n t his case it indicat es t he num ber of init ial colum ns t hat will be hidden. Nam ed const ant s can also serve all of t he sam e purposes on a worksheet t hat VBA const ant s serve in a VBA program , as discussed in Chapt er 3 Excel and VBA Developm ent Best Pract ices.
Two part icularly im port ant uses of nam ed const ant s are workbook ident ificat ion and version ident ificat ion. Each UI workbook you creat e should have a unique nam ed const ant t hat ident ifies it as belonging t o your applicat ion. The add- in for your applicat ion can t hen use t his const ant t o det erm ine whet her t he current ly act ive workbook belongs t o it . You should also include a version const ant so you can pinpoint exact ly what version of your applicat ion a given workbook belongs t o. This becom es very im port ant when you upgrade t he applicat ion such t hat prior version user int erface workbooks m ust be updat ed in som e way.
Named Ranges Nam ed ranges enable you t o reference a locat ion on a worksheet wit h a friendly nam e t hat conveys inform at ion about t hat locat ion, rat her t han using a range address t hat cannot be int erpret ed wit hout following it back t o t he cell or cells it refers t o. As t he exam ple below shows, nam ed ranges also enable you t o accom plish t hings you cannot accom plish wit h direct ly ent ered cell addresses. Everyone reading t his book should be fam iliar wit h fixed nam ed ranges, t hose referring t o a fixed cell or group of cells on a worksheet . This sect ion concent rat es on t he less well- underst ood t opic of relat ive nam ed ranges. A r e la t ive nam ed range is called relat ive because t he locat ion it references is det erm ined relat ive t o t he cell in which t he nam e is used. Relat ive nam ed ranges are defined in such a way t hat t he cell or cells t hey refer t o change depending on where t he nam e is used. There are t hree t ypes of relat ive nam ed ranges:
1 . Colu m n r e la t ive The referenced colum n changes, but t he referenced row rem ains fixed. These can be ident ified because t he absolut e reference sym bol ( $) appears only before t he row num ber. The address A$1 is an exam ple of a colum n- relat ive address. 2 . Row r e la t ive The referenced row changes, but t he referenced colum n rem ains fixed. These can be ident ified because t he absolut e reference sym bol ( $) appears only before t he colum n let t er. The address $A1 is an exam ple of a row- relat ive address. 3 . Fu lly r e la t ive Bot h t he referenced row and t he referenced colum n change. I n fully relat ive nam ed ranges, neit her t he row nor t he colum n is prefixed wit h t he absolut e reference sym bol ( $) . The address A1 is an exam ple of a fully relat ive address. To creat e a relat ive nam ed range, you m ust first select a cell whose posit ion you will define t he nam e relat ive t o. This cell is your st a r t in g poin t . This cell is not t he only cell where t he nam e can be used; it sim ply gives you a point from which t o define t he relat ive nam e. I n t he next exam ple, we dem onst rat e how t o define and use a fully relat ive nam ed range t hat enables you t o creat e form ulas t hat aut om at ically adj ust t he range t hey refer t o when a row is insert ed direct ly above t hem . First let 's see why t his is im port ant . Figure 4- 3 shows a sim ple t able showing t he sales for t hree hypot het ical regions. The t ot al sales for all t hree regions are calculat ed using t he built - in SUM worksheet funct ion, which you can see displayed in t he form ula bar.
Figu r e 4 - 3 . Tot a l Sa le s Usin g a St a n da r d For m u la
Now assum e we need t o add a fourt h region t o our list . We will insert a new row direct ly above t he Tot al Sales row and add Region D. Figure 4- 4 shows t he result .
Figu r e 4 - 4 . I n se r t a n Addit ion a l Re gion t o t h e List
Because t he new region was insert ed at t he bot t om of t he list , t he SUM funct ion range did not adj ust and t he Tot al Sales num ber report ed by t he funct ion is now wrong. This exam ple was designed t o m ake t he problem blindingly obvious. I n real- world worksheet s, t his t ype of m ist ake is frequent and rarely so obvious. I n fact , it is one of t he m ost com m on errors we discover when audit ing m alfunct ioning worksheet s. This error is easy t o avoid by defining a fully relat ive nam ed range t hat always refers t o t he cell direct ly above t he cell where t he nam e is used. To do t his, choose I nsert > Nam e > Define t o display t he Define Nam e dialog ( or bet t er yet , use t he Ct rl+ F3 keyboard short cut ) . As you can see in Figure 4- 5 , our st art ing point is cell B6 and we have defined a fully relat ive, sheet - level nam ed range called pt rCellAbove t hat refers t o cell B5.
Figu r e 4 - 5 . Cr e a t in g a Fu lly Re la t ive N a m e d Ra n ge [View full size image]
Next we m odify our SUM funct ion so it references t he pt rCellAbove nam ed range rat her t han a specific ending cell address, as shown in Figure 4- 6 .
Figu r e 4 - 6 . Usin g a Fu lly Re la t ive N a m e d Ra n ge in a W or k sh e e t Fu n ct ion
Not only does our SUM funct ion now display t he correct answer, you can insert as m any rows direct ly above it as you like and it will always sum t he correct area. This feat can only be accom plished t hrough t he use of a fully relat ive nam ed range. We use relat ive nam ed ranges ext ensively in our sam ple applicat ion.
Named Formulas The least underst ood and m ost powerful defined nam e t ype is t he nam ed form ula. Nam ed form ulas are built from t he sam e Excel funct ions as regular worksheet form ulas and like worksheet form ulas t hey can ret urn sim ple values, arrays and range references. Nam ed form ulas enable you t o package up com plex but frequent ly used form ulas int o a single defined nam e. This m akes t he form ula m uch easier t o use, because all you need t o do is ent er t he defined nam e you've assigned t o it rat her t han t he ent ire form ula. I t also m akes t he form ula easier t o m aint ain because you can m odify it in one place ( t he Define Nam e dialog) and t he changes will aut om at ically propagat e t o every cell where t he defined nam e is used. I n t he Pract ical Exam ple sect ion of t his chapt er, we show an exam ple of how t o use a nam ed form ula t o package a com plex worksheet form ula int o a defined nam e t o m ake it m ore m aint ainable and easier t o use. Nam ed form ulas can also be used t o creat e dyn a m ic list s. A dynam ic list form ula is used t o ret urn a reference t o a list of ent ries on a worksheet when t he num ber of ent ries in t he list is variable. Worksheet user int erface developm ent m akes ext ensive use of dynam ic list s for dat a- validat ion purposes, a t opic we cover in dept h in t he Dat a Validat ion sect ion lat er in t he chapt er, but let 's revisit t he t im esheet from Figure 4- 1 t o show a quick exam ple. I n t his t ype of user int erface, we wouldn't want users t o ent er what ever act ivit y nam e t hey want in t he Act ivit y colum n. To m ake our dat a consist ent from user t o user, we would define a dat avalidat ion list of accept able act ivit y nam es and users would pick t he act ivit y t hat m ost closely described what t hey were doing from our predefined dat a- validat ion list . We'll put our act ivit y list on a background worksheet ( one not designed t o be seen by t he user) and creat e a dynam ic list nam ed form ula t hat refers t o it . Figure 4- 7 shows t his nam ed form ula.
Figu r e 4 - 7 . A D yn a m ic N a m e d For m u la [View full size image]
The valAct ivit iesList nam ed form ula can now be used as t he dat a- validat ion list for t he t im esheet Act ivit y colum n. A dynam ic list nam ed form ula consist s of t he following part s: St a r t in g poin t The point at which t he list begins. I n t his case, our st art ing point is cell w ksDat a! $A$1. D a t a a r e a The full range in which it em s of our list m ight be locat ed. This includes not only cells t hat are current ly being used, but also cells t hat m ight be used in t he fut ure. I n t his case, our dat a area is t he ent ire colum n A, or wksDat a! $A: $A. List for m u la A form ula t hat det erm ines t he num ber of it em s current ly in t he list and ret urns a range reference t o j ust t hose it em s. This is a com binat ion of t he OFFSET and COUNTA worksheet funct ions.
Scope of Defined Names Defined nam es can have one of t wo scopes: worksheet level or workbook level. These are roughly analogous t o privat e and public variables. Like variables, defined nam es should be given t he m ost lim it ed scope possible. Always use worksheet - level defined nam es unless you m ust m ake a nam e workbook level. When your workbook cont ains a large num ber of defined nam es, using worksheet - level defined nam es helps reduce t he num ber of nam es you have t o look t hrough in t he Define Nam e dialog all at once. Worksheet - level defined nam es can be used from ot her worksheet s in m ost cases. When t hey are used from anot her worksheet , t hey are j ust prefixed wit h t he nam e of t he worksheet from which t hey originat ed. This m akes audit ing worksheet s t hat use defined nam es m uch sim pler because you don't have t o look up every defined nam e you com e across in t he Define Nam es dialog in order t o det erm ine which worksheet it references. I t is also oft en useful t o have t he sam e defined nam e on m ult iple worksheet s in your user int erface workbook. Two good exam ples of t his are general- purpose, fully relat ive range nam es such as t he pt rCellAbove range we discussed earlier and nam es t hat hold t he values of set t ings you want t o m ake t o each worksheet using VBA code. We cover t he lat t er in m ore det ail in Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins. Som e circum st ances require you t o use workbook- level defined nam es. Figure 4- 7 dem onst rat es t he m ost com m on case. A defined nam e t hat refers t o a range locat ed on a different worksheet t hat you want t o use in a dat a- validat ion list m ust be a workbook- level defined nam e. This is a lim it at ion inherent in Excel's dat a- validat ion feat ure. I n som e cases, a workbook- level defined nam e is sim ply appropriat e, such as when t he nam e t ruly refers t o t he ent ire workbook rat her t han t o any individual worksheet . This would be t he case wit h a nam ed const ant used t o ident ify t he version num ber of a workbook. I n t he pract ical exam ple sect ion of Chapt er 7 Using Class Modules t o Creat e Obj ect s, we dem onst rat e t he use of a workbooklevel defined const ant t o ident ify workbooks t hat belong t o our applicat ion.
Styles Advantages of Styles St yles provide a num ber of advant ages t hat m ake t hem an int egral part of any worksheet user int erface. They provide a sim ple, flexible way t o apply sim ilar form at t ing t o all t he cells in your worksheet user int erface t hat serve a sim ilar purpose. The consist ent use of st yles also gives t he user clear visual clues about how your user int erface works. Using our t im esheet exam ple from Figure 4- 1 , Figure 4- 8 shows how different st yles define different areas of t he worksheet user int erface.
Figu r e 4 - 8 . Usin g St yle s a s Visu a l I n dica t or s of t h e St r u ct u r e of You r Use r I n t e r fa ce [View full size image]
St yles enable you t o apply t he m ult iple form at t ing charact erist ics required for each user int erface range all at once. Form at t ing charact erist ics com m only applied t hrough t he use of st yles include num ber form at , font t ype, background shading and cell prot ect ion. Ot her st yle propert ies, such as t ext alignm ent and cell borders, are less com m only used because t hey t end t o be different , even wit hin cells of t he sam e st yle. Cust om st yles, which we discuss in t he next sect ion, can be configured t o ignore t he form at t ing charact erist ics you don't want t o include in t hem .
I f you need t o change t he form at of a cert ain area of your user int erface, you can j ust m odify t he appropriat e st yle and all of t he cells using t hat st yle will updat e aut om at ically. Here's an all- t oocom m on real- world exam ple of where t his is very useful. You've creat ed a com plex, m ult isheet dat a- ent ry workbook using whit e as t he background color for dat a- ent ry cells. When you show t his t o your client or boss, t hey decide t hey want t he dat a- ent ry cells t o be shaded light yellow inst ead of whit e. I f you didn't use st yles t o const ruct your user int erface, you would have t o laboriously reform at every dat a- ent ry cell in your workbook. I f you did use st yles, all t hat 's required is t o change t he pat t ern color of your dat a- ent ry st yle from whit e t o light yellow and every dat a- ent ry cell in your workbook will updat e aut om at ically. Given t he frequency wit h which people change t heir m inds about how t heir applicat ions should look, using st yles t hroughout an applicat ion can save you a significant am ount of t im e and effort .
Creating and Using Styles Adding cust om st yles is not t he m ost int uit ive process in Excel, but aft er you've seen t he st eps required, you'll be creat ing st yles like an expert in no t im e. Cust om st yles are creat ed using t he Form at > St yle m enu. This opens t he St yle dialog, shown in Figure 4- 9 , from which all st yle confusions originat e.
Figu r e 4 - 9 . Th e Ex ce l St yle D ia log
When t he St yle dialog first opens, it aut om at ically displays t he form at t ing charact erist ics of t he cell t hat was select ed when t he dialog was invoked. I n Figure 4- 9 , t he St yle dialog was invoked while t he select ed cell was in t he St art Tim e colum n shown in Figure 4- 8 . As you can see, t his cell was form at t ed wit h t he I nput st yle, so t his is t he st yle displayed by t he St yle dialog.
To creat e a new st yle, ent er t he nam e of t he st yle you want t o creat e in t he St yle nam e com bo box, as shown in Figure 4- 10.
Figu r e 4 - 1 0 . A N e w St yle I s Alw a ys Ba se d on t h e St yle of t h e Ce ll Se le ct e d W h e n t h e St yle D ia log I s D ispla ye d
Aft er you do t his, you will encount er one of t he m ore confusing aspect s of t he St yle dialog. All t he St yle I ncludes check boxes will be checked and t heir values will be set t o t he form at of t he cell t hat was select ed when t he St yle dialog was invoked. This occurs even if t hose form at charact erist ics are not part of t he st yle current ly applied t o t hat cell. For exam ple, Num ber, Alignm ent and Border at t ribut es were excluded from t he I nput st yle t hat was displayed in t he St yle dialog im m ediat ely before we creat ed our new st yle. All t hree of t hose at t ribut es are included in our new st yle, however, and t heir specific values are drawn from t he form at t hat was applied t o t he cell t hat was select ed when t he St yle dialog was first invoked. This is what t he By Exam ple in parent hesis aft er t he St yle I ncludes t it le m eans. Don't worry; all of t hese at t ribut es can easily be changed. First , rem ove t he checkm ark from beside any form at opt ion t hat you don't want t o include in your st yle. When a st yle is applied t o a range, only t he form at opt ions you checked will be applied. Next , click t he Modify but t on t o define t he propert ies of your new st yle. This will display t he Form at Cells dialog, shown in Figure 4- 11.
Figu r e 4 - 1 1 . Th e For m a t Ce lls D ia log a s I n vok e d fr om t h e St yle D ia log M odify Bu t t on
Not ice t hat t he six t abs on t he Form at Cells dialog correspond exact ly t o t he six St yle I ncludes opt ions shown in Figure 4- 10. This is no accident . St yles are j ust a way of grouping m ult iple cell form at charact erist ics under a single nam e so t hat t hey can be applied and m aint ained sim ult aneously t hrough t hat nam e.
N OTE I f you rem ove t he checkm ark from a St yle I ncludes opt ion but t hen change any of t he charact erist ics of t hat opt ion in t he Form at Cells dialog, t he opt ion will aut om at ically becom e checked again in t he St yle dialog.
Modifying Styles Modifying an exist ing st yle is exact ly like creat ing a new st yle except t hat aft er select ing t he Form at > St yle m enu, you pick t he st yle you want t o m odify from t he St yle nam e com bo box rat her t han ent ering a new st yle nam e. Each t im e you select a st yle in t he St yle nam e com bo box, t hat st yle will have it s set t ings sum m arized and displayed for you in t he St yle I ncludes sect ion of t he dialog. Click t he Modify but t on t o display t he Form at Cells dialog and change any of t he form at opt ions for
t he current ly select ed st yle. There is one m inor caut ion t o keep in m ind when creat ing new st yles or m odifying exist ing st yles. Aft er you have configured t he st yle using t he Form at Cells dialog, be sure t o click t he Add but t on on t he St yle dialog t o save your changes. I f you click t he OK but t on, your changes will be saved, but t he st yle you have creat ed or m odified will also be applied t o t he current ly select ed cell. This is oft en not t he result you want . Get t ing int o t he habit of using t he Add but t on t o add and updat e st yles will save you from having t o undo changes t o a cell you didn't int end t o change. Aft er you have used t he Add but t on t o creat e or m odify a St yle, you can safely use t he Cancel but t on t o dism iss t he St yle dialog wit hout losing your work or form at t ing t he current ly select ed cell.
Adding the Style Drop-Down to the Toolbar I f you're fam iliar wit h Word, you'll not ice st yles in Word are considered so im port ant t hat a special st yle drop- down is aut om at ically present on t he Form at t ing t oolbar. This not only enables you t o quickly apply a st yle t o a select ion, but also displays t he st yle associat ed wit h t he sect ion of t he docum ent where your cursor is locat ed. Excel has t he sam e t oolbar cont rol, but for som e reason, st yles in Excel were not deem ed im port ant enough by Microsoft t o have t his cont rol appear by default . You can add t his cont rol t o one of your Excel t oolbars m anually, however, and if you plan on m aking full use of st yles in Excel you should do so. Here's how. St art by select ing View > Toolbars > Cust om ize from t he Excel m enu. I n t he Cust om ize dialog, select t he Com m ands t ab. I n t he Com m ands t ab, select t he Form at it em from t he Cat egor ies list . As shown in Figure 4- 12, t he St yle drop- down will be t he fift h it em in t he Com m ands list box.
Figu r e 4 - 1 2 . Se le ct in g t h e St yle D r op- D ow n fr om t h e List of For m a t Con t r ols
Drag t his cont rol from t he Com m ands list box and drop in ont o one of your exist ing t oolbars. You will now have a St yle cont rol t hat provides t he sam e benefit s as t he St yle cont rol in Word. You can select a group of cells and apply a st yle t o all of t hose cells by sim ply select ing t he st yle nam e from t he St yle drop- down. And when you select a cell, t he nam e of t he st yle applied t o t hat cell will aut om at ically be displayed in t he St yle drop- down. This feat ure proves very helpful when creat ing com plex worksheet user int erfaces t hat ut ilize m any different st yles.
User Interface Drawing Techniques Using Borders to Create Special Effects To keep t he user focused on t he elem ent s of your worksheet user int erface, it is oft en helpful t o m odify t he norm al st yle so all unused areas of t he worksheet have a consist ent , light gray background color. This pract ice has been dem onst rat ed in m ost of t he user int erface exam ples shown so far and will be used in our sam ple applicat ion. On t op of t his light gray background, you can use cell borders t o creat e som e int erest ing special effect s. One of t he m ost com m only used border- based special effect s gives a range of cells a 3D appearance, eit her raised or sunken. Figur e 4- 13 shows exam ples of bot h effect s.
Figu r e 4 - 1 3 . Usin g Bor de r s t o Cr e a t e 3 D Visu a l Effe ct s
To creat e a raised effect , you j ust add a whit e border t o t he t op and right sides of your range and add a 50 percent gray border t o t he left and bot t om sides of your range. To creat e a sunken effect , you do exact ly t he opposit e. The widt h of t he borders can be used t o cont rol t he degree of t he effect . When you've applied a background color t o a worksheet , as we've done in t he exam ple above, Excel's st andard gridlines are obscured. I n m any cases gridlines are a useful visual guide for t he user, so you want t o put t hem back. Alt hough t here is no way t o force Excel's st andard gridlines t o display over a background color, you can easily sim ulat e gridlines by adding 25 percent gray borders wit h t he light est widt h t o t he area where you want t he gridlines t o appear. Figure 4- 14 shows t his effect .
Figu r e 4 - 1 4 . Usin g Bor de r s t o Sim u la t e Gr idlin e s
Creating Well-Formatted Tables Tables used wit hin an Excel worksheet user int erface t ypically have one or m ore of t he following elem ent s: Table descript ion Row and colum n descript ions Dat a- ent ry area Form ula result area Each sect ion of your t able should be form at t ed wit h a unique st yle t hat you use consist ent ly t hroughout your user int erface. Figure 4- 15 shows a sam ple t able wit h all four of t he elem ent s described above.
Figu r e 4 - 1 5 . A Ba sic W or k sh e e t Use r I n t e r fa ce Ta ble La you t
As you can see, in it s sim plest form t he t able is not very at t ract ive. You can give your t ables a m uch m ore professional appearance by using borders t o provide a 3D effect and sim ulat ed gridlines and by increasing t he row height s and colum n widt hs t o provide m ore visual separat ion. Turning off t he row and colum n headers and t he form ula bar com plet es t he effect . The t able now looks like a com plet ely cust om user int erface. Figure 4- 16 shows t he t able wit h t hese added effect s.
Figu r e 4 - 1 6 . A Fu lly For m a t t e d W or k sh e e t Use r I n t e r fa ce Ta ble
Cell Comments for Help Text Cell com m ent s are one of t he m ost im port ant user int erface feat ures provided by Excel. Their ut ilit y st em s from t he fact t hat in m any cases t hey can serve t he sam e purpose as a help file wit hout
requiring users t o do anyt hing m ore com plicat ed t han hover t heir m ouse cursor over t he com m ent ed cell. Not e t hat cell com m ent s have several lim it at ions t hat m ay m ake t hem inappropriat e in cert ain sit uat ions: I f you are using t he freeze panes feat ure on a worksheet and t he worksheet is scrolled beyond t he freeze point , if t he com m ent window overlaps t he frozen row and/ or colum n it will be cut off at t he point where t he window is frozen. Each cell com m ent is also associat ed wit h a specific st at us bar m essage whose st ruct ure cannot be m odified. The st at us bar m essage displayed when users hover t heir m ouse over a com m ent has t he following st ruct ure, which is shown graphically in Figure 4- 17:
Figu r e 4 - 1 7 . Th e For m a t of a n Ex ce l Com m e n t St a t u s Ba r M e ssa ge
Cell a d d r e ss com m ent ed by u se r n a m e a t t h e t im e t h e com m e n t w a s cr e a t e d The only part of t his m essage you can m odify is t he user nam e at t he t im e t he com m ent was creat ed sect ion, which displays t he cont ent s of t he User nam e ent ry locat ed under t he Tools > Opt ions > General t ab of t he Excel m enu. I f you are a consult ant creat ing a worksheet user int erface for a client , it 's unlikely your client will want t o see your nam e in t he st at us bar each t im e t hey view a cell com m ent . I n t hat case, one of t he best workarounds is t o change t he User nam e set t ing on your m achine t o your client 's com pany nam e while you creat e t he com m ent s for t heir user int erface. Aft er t he com m ent s have been creat ed, t he user nam e displayed in t he st at us bar is fixed and will not be affect ed when you change your User nam e set t ing back t o your own nam e. Rem em ber t hat cell com m ent s can be rich- t ext form at t ed. This m eans you can use form at t ing such as bold and it alic font s wit hin t he com m ent t ext as well as m ult iple font s. Rich- t ext form at t ing enables you t o creat e som e very sophist icat ed help m essages. Figure 4- 18 shows a rich- t ext form at t ed cell com m ent from a real- world worksheet user int erface.
Figu r e 4 - 1 8 . A Rich - Te x t - For m a t t e d Ce ll Com m e n t
Using Shapes The abilit y t o use shapes ( obj ect s drawn using t he various opt ions on t he Drawing or Form s t oolbars) on an Excel worksheet is a very powerful user int erface t echnique. Shapes are locat ed in a special drawing layer t hat exist s above t he cells on a worksheet , so shapes cover ( and obscure) worksheet cells. Shapes are also connect ed t o t he underlying worksheet t hrough t heir propert ies, which allow t hem t o do t he following: Move and size wit h t he worksheet cells t hey cover Move but don't size wit h t he worksheet cells t hey cover Don't m ove or size wit h t he worksheet cells t hey cover Alm ost all shapes can cont ain t ext . A shape's t ext can eit her be m anually ent ered or it can be linked dynam ically t o a specific cell on a worksheet by select ing t he shape and ent ering t he address of t hat cell as a form ula in t he form ula bar. As you can im agine, t he abilit y t o assign form ulas t o shapes opens up a wide array of opt ions for creat ing dynam ic user int erfaces. Shapes can also be given a m acro assignm ent t hat will cause t hem t o execut e t he specified m acro whenever t he user clicks t hem . Just right - click over t he shape and choose Assign Macro from t he short cut m enu. Figure 4- 19 shows an excellent exam ple of how shapes can be used t o creat e a cust om t oolbar- like area across t he t op of a worksheet user int erface.
Figu r e 4 - 1 9 . A Cu st om On - Sh e e t Toolba r Cr e a t e d w it h Sh a pe s [View full size image]
Data Validation Dat a validat ion is one of t he m ost useful yet underut ilized feat ures for worksheet user int erface design. I t enables you t o ensure t hat m ost , if not all, of t he input s m ade in your user int erface are correct by disallowing input t hat does not m at ch t he rules you specify. Dat a validat ion can be as sim ple as rest rict ing cell ent ries t o whole num bers or as com plex as rest rict ing cell ent ries t o it em s on a list whose cont ent s are condit ionally det erm ined based on an ent ry m ade in a previous cell. We assum e you underst and t he basic use of dat a validat ion and inst ead dem onst rat e t wo of t he m ore com plex validat ion scenarios t hat can be creat ed wit h t his feat ure. Most com plex dat a validat ion scenarios involve dat a validat ed list s or cust om dat a validat ion form ulas.
Unique Entries I f you need t he user t o ent er only unique it em nam es in a dat a- ent ry list , you can use a cust om dat a- validat ion form ula t o enforce uniqueness. First select t he ent ire dat a- ent ry area you need t o validat e. Then choose Dat a > Validat ion from t he m enu and select t he Cust om opt ion from t he Allow list . The basic synt ax of t he form ula you need t o ent er is t he following:
=COUNTIF(,)=1
The first argum ent t o t he COUNTI F funct ion is a fixed reference t o t he ent ire dat a- ent ry area t hat m ust cont ain unique ent ries. The second argum ent t o t he COUNTI F funct ion is a relat ive reference t o t he current ly select ed cell in t he dat a- input range. I f each ent ry is unique, t he COUNTI F funct ion evaluat es t o 1 and t he ent ire form ula evaluat es t o True, m eaning t he dat a is valid. I f t he COUNTI F funct ion locat es m ore t han one inst ance of an ent ry in t he dat a- ent ry area t he ent ire form ula will evaluat e t o False and dat a validat ion will prevent t hat ent ry from being m ade. Figure 4- 20 shows an exam ple of t his validat ion set up and Figure 4- 21 shows it in act ion.
Figu r e 4 - 2 0 . D a t a Va lida t ion Con figu r a t ion t o For ce Un iqu e En t r ie s in a List [View full size image]
Figu r e 4 - 2 1 . Un iqu e En t r ie s D a t a Va lida t ion in Act ion
Cascading Lists I n t his t ype of validat ion, t he specific dat a validat ion list t hat is displayed for a cell is det erm ined by t he ent ry select ed in a previous cell. I n Figure 4- 22, t he dat a validat ion list for t he I t em colum n is det erm ined by t he select ion in t he Cat egory colum n. All of t he dat a validat ion list s are locat ed in t he hidden colum n A. The Cat egories list is t he dat a validat ion list for t he Cat egory colum n. The Fruit s list is t he dat a validat ion list for t he I t em colum n when t he Cat egory select ed is Fruit s. The
Veget ables list is t he dat a validat ion list for t he I t em colum n when t he Cat egory select ed is Veget ables.
Figu r e 4 - 2 2 . I n it ia l Se t u p for Ca sca din g D a t a - Va lida t ion List s
The dat a validat ion list form ula for t he Cat egory colum n is sim ple: =$A$3:$A$4. The dat a validat ion list form ula for t he I t em colum n is a bit m ore com plicat ed. I t has t o check t he value of t he corresponding Cat egory ent ry and do one of t hree t hings: display no list if t he Cat egory ent ry has not been select ed, display t he list of fruit s if Fruit s has been select ed or display t he list of veget ables if Veget ables has been select ed. The form ula t hat does all t his is shown below:
=IF(C3=$A$3,$A$7:$A$10,IF(C3=$A$4,$A$13:$A$16,$A$1)))
Not e t he t railing reference t o cell $A$1 in t his form ula. The purpose t his serves is t o creat e an em pt y validat ion list in t he I t em colum n if not hing has been select ed in t he Cat egory colum n. I t wouldn't m ake sense t o allow t he user t o select an I t em prior t o select ing a Cat egory. As Figure 423 shows, t his form ula successfully displays t wo com plet ely different dat a validat ion list s depending on t he cat egory select ion.
Figu r e 4 - 2 3 . Ca sca din g List Va lida t ion in Act ion
This logic can be ext ended t o as m any cat egories as t he m axim um form ula lengt h of 1024
charact ers will allow. However, for cases wit h large num bers of cat egories, a t able- driven approach t hat you'll see used in our sam ple t im esheet applicat ion is m uch easier t o set up and m aint ain. Not e t hat one drawback of t his t ype of validat ion is t hat it doesn't work in bot h direct ions. I n t he scenario described above, t here is not hing t o st op a user from accident ally changing t he cat egory ent ry in a row where a specific it em has already been select ed. I n t he next sect ion we show how t o use condit ional form at t ing t o provide a visual indicat ion t hat t his kind of error has been m ade.
N OTE I n Excel 97 t here is a bug such t hat in m ost circum st ances, dat a- validat ion list s will not funct ion when t he freeze panes feat ure has been applied t o a worksheet .
Conditional Formatting Condit ional form at t ing is one of t he m ost powerful feat ures available for Excel user int erface const ruct ion. I t enables you t o subst it ut e sim ple form ulas for what would ot herwise be ream s of VBA code. Condit ional form at t ing works by m odifying t he appearance of t he cells it has been applied t o only if one or m ore condit ions t hat you specify have been m et . Condit ional form at t ing overrides any st yle set t ing when t he condit ion is t riggered. When t he condit ion t hat t riggered t he condit ional form at t ing is no longer t rue, t he affect ed cell regains it s original form at . The t wo m ost com m on uses for condit ional form at t ing in Excel user int erface developm ent are t he creat ion of dynam ic t ables and calling out error condit ions.
Creating Dynamic Tables When building nont rivial worksheet - based user int erfaces, you will oft en be faced wit h t he problem of providing a t able t hat in ext rem e cases will allow t he ent ry of som e large num ber of rows but for t he m ost com m on scenarios will only require a few. Rat her t han hard- coding a visible t able wit h t he m axim um possible num ber of rows, you can use condit ional form at t ing t o creat e a t able t hat expands dynam ically as dat a is ent ered int o it . We dem onst rat e how t his is done beginning wit h t he sam ple t able shown in Figure 4- 24.
Figu r e 4 - 2 4 . D a t a - En t r y Ta ble Pr ior t o t h e Addit ion of D yn a m ic For m a t t in g
Let 's assum e t his t able really requires 200 rows for t he largest proj ect s but t hat m ost users only need a few rows of input . Therefore, you want t o hide t he unused area of t he t able. As you can see, t he first st ep in creat ing a dynam ic t able is t o draw t he ent ire t able on t he worksheet . You t hen use condit ional form at t ing t o hide t he unused area of t he t able and reveal rows dynam ically as needed. The t rigger for displaying a dat a- ent ry row will be t he user ent ering a new nam e int o t he I t em Nam e colum n. For t hat reason, we always need t o leave an em pt y I t em Nam e ent ry cell at t he bot t om of t he t able. When creat ing a dynam ic t able, it 's a good idea t o also creat e an out line showing t he ext ent of t he t able in one of your hidden colum ns. Aft er we've added t he condit ional form at t ing, t he t able will disappear. This m akes t he t able difficult t o m aint ain if you haven't provided yourself wit h a visual m arker indicat ing it s ext ent . The em pt y bordered area in colum n A serves t his purpose in our exam ple. This area doesn't need t o be em pt y. I t could include error- checking form ulas, for exam ple. As long as it gives you a visual indicat ion of t he ext ent of t he hidden area of t he t able, it serves it s purpose. Our dynam ic t able requires t hree different condit ionally form at t ed sect ions. Referencing Figure 425 , t he first sect ion will encom pass range C3:C12, t he second sect ion will encom pass range D3:F12 and t he t hird range will encom pass range G3:G12. We'll add t he condit ional form at s one st ep at a t im e so you can see t he result s as t hey occur. To m ake t he operat ion of t he condit ional form at s m ore obvious we'll add dat a t o t he first row of t he t able. Keep in m ind t hat t he purpose of all t hree condit ional form at t ing sect ions is t he sam e: t o sim ulat e t he appearance of a t able t hat is j ust large enough t o hold t he dat a t hat has been ent ered int o it . Figure 4- 25 shows t he t able wit h t he first sect ion of condit ional form at t ing com plet ed.
Figu r e 4 - 2 5 . Con dit ion a l For m a t t in g for t h e Fir st Colu m n [View full size image]
I n addit ion t o t he purpose described above, t he first condit ional form at serves t o leave a blank cell in front of t he first unused t able row in order t o help prom pt t he user t o ent er t he next it em . The second condit ional form at is shown in Figure 4- 26. I t clears all unused rows in colum ns D t hrough F and draws a bot t om border below t he first unused row in t he t able, t hereby helping t o com plet e t he t able out line.
Figu r e 4 - 2 6 . Con dit ion a l For m a t t in g for t h e Re m a in in g Colu m n s W it h in t h e Ta ble [View full size image]
You can see t he whit e border on t he far right side of t he t able is m issing in Figure 4- 27. The purpose of t he t hird condit ional form at is t o com plet e t he sim ulat ed t able by drawing t his border. Figure 4- 27 shows t he t hird condit ional form at .
Figu r e 4 - 2 7 . Con dit ion a l For m a t t in g Ou t side t h e Ta ble t o Cr e a t e t h e Righ t - H a n d Bor de r [View full size image]
Figure 4- 28 shows t he fully form at t ed t able wit h som e addit ional ent ries. Each t im e a new ent ry is m ade, t he condit ional form at reveals t he row in which t he ent ry was placed and adds a new prom pt row below it .
Figu r e 4 - 2 8 . Th e Com ple t e D yn a m ica lly For m a t t e d Ta ble
The one m aj or caveat when considering t he use of condit ional form at t ing t o creat e dynam ic t ables is t hat calculat ion m ust be set t o aut om at ic in order for it t o work. I f your user int erface is so calculat ion int ensive t hat you need t o set calculat ion t o m anual, t hen you cannot creat e dynam ic t ables using t his m et hod ( or use any ot her t ype of form ula- based condit ional form at t ing for t hat m at t er) .
Calling Out Error Conditions Condit ional form at t ing can also work alone or in concert wit h form ulas in hidden rows and colum ns t o highlight invalid ent ries as soon as t hey are m ade. This should not be your m et hod of first choice for point ing out dat a- ent ry errors. Always t ry t o use dat a validat ion t o prevent dat a- ent ry errors from being m ade in t he first place. The m ost com m on sit uat ion in which errors cannot be prevent ed by dat a validat ion is when you have t wo dat a- ent ry colum ns such t hat t he ent ry in t he first colum n det erm ines t he allowable ent ries in t he second colum n. I n Figure 4- 29 we revisit our cascading dat a- validat ion list exam ple fr om Figure 4- 22.
Figu r e 4 - 2 9 . Th e Er r or Ch e ck For m u la Colu m n for t h e Con dit ion a l For m a t
Even t hough bot h colum ns' list s are dat a validat ed, an error can creep in if t he user init ially select s a valid cat egory and it em com binat ion but t hen accident ally changes t he cat egory nam e at som e lat er point in t im e. This t ype of m ist ake cannot be prevent ed by dat a validat ion, so we need t o provide som e visual indicat ion t hat t here is a m ism at ch bet ween t he cat egory and it em select ions if t his error occurs. This is a t ask for condit ional form at t ing. As you can see in Figure 4- 29, we've insert ed a second hidden colum n. I n t his colum n we've creat ed an error check for each row t hat verifies t he ent ry select ed in t he I t em colum n is valid for t he select ion in t he Cat egory colum n.
The error check form ula is a bit com plicat ed, so we break it down in List ing 4- 1 . Keep in m ind t hat t he purpose of t he error check form ula is t o ret urn True if t he corresponding row in t he t able has a dat a- ent ry error and False ot herwise.
List in g 4 - 1 . Th e Er r or Ch e ck For m u la Ou t lin e d =IF(ISBLANK(E3),FALSE, IF(D3=$A$3, ISERROR(MATCH(E3,$A$7:$A$10,0)), ISERROR(MATCH(E3,$A$13:$A$16,0)) ) )
The only t ype of error t hat can occur in t his sit uat ion is t he I t em colum n ent ry not m at ching t he Cat egory colum n ent ry. I f t here is no I t em colum n ent ry, t he row is not com plet e and we cannot det erm ine t he validit y of t he Cat egory colum n ent ry. The out er I F funct ion checks for t his condit ion and ret urns FALSE if t his is t he case. When t here is an ent ry in t he I t em colum n, t he inner I F funct ion det erm ines t he correct list from which t o t ry and locat e t he Cat egory ent ry. The form ula t hen uses t he MATCH funct ion wrapped in t he I SERROR funct ion t o ret urn TRUE if t he Cat egory ent ry is locat ed in t he correct list or FALSE if it isn't . The next t hing we do is add a condit ional form at t o t he t able t hat checks t he value of t he HasError colum n. I f t he HasError colum n indicat es t here is an error in one of t he t able rows, our condit ional form at will give t hat row a bright red shade. Error condit ion highlight ing is one except ion t o t he rule of not using garish colors in your user int erface. We do recom m end using red, however, because t his is alm ost universally recognized as a warning color. Figure 4- 30 shows t he condit ional form at required t o accom plish t his.
Figu r e 4 - 3 0 . Se t t in g Up Con dit ion a l For m a t t in g t o Fla g a n Er r or Con dit ion [View full size image]
The result of t he condit ional form at in response t o an error condit ion is shown in Figure 4- 31, where we've changed t he Cat egory colum n ent ry in t he second t able row from Veget ables t o Fruit s so it no longer m at ches t he ent ry in t he I t em colum n.
Figu r e 4 - 3 1 . Con dit ion a l For m a t t in g Fla ggin g a Ba d En t r y in t h e Ta ble
Using Controls on Worksheets Using cont rols placed direct ly on worksheet s is t ypically not t he best user int erface design. For m ost Excel applicat ion developm ent , we recom m end you use cust om com m and bars as ent ry point s int o your code and subst it ut e dat a- validat ion list s for com bo box cont rols. ( Com m and bars are given a full- chapt er t reat m ent in Chapt er 8 Advanced Com m and Bar Handling.) I n som e circum st ances, placing cont rols direct ly on your worksheet user int erface is t he best opt ion, so here we cover som e of t he t hings you need t o wat ch out for when you do t his. When you do need t o use cont rols on a worksheet , you m ust choose bet ween Act iveX cont rols and cont rols from t he Form s t oolbar. As a general rule, we recom m end you use Form s cont rols unless you absolut ely need Act iveX cont rols. Form s cont rols are very light weight and don't exhibit t he m any quirks you'll run int o when using Act iveX cont rols on worksheet s. Figure 4- 32 shows a worksheet in which Form s cont rols have been used t o great effect .
Figu r e 4 - 3 2 . Good Use of For m s Con t r ols on a W or k sh e e t [View full size image]
Because everyone reading t his chapt er should be fam iliar wit h how cont rols work, we j ust cover t he det ails crit ical t o deciding whet her you can use Form s cont rols in your worksheet user int erface or whet her you need Act iveX cont rols.
Advantages of Forms Controls Form s cont rols can be used on Chart sheet s, Act iveX cont rols cannot . Form s cont rols are m ore t ight ly linked t o Excel. You can select a Label or But t on cont rol and ent er a form ula in t he form ula bar t hat will dynam ically set t he capt ions of t hose cont rols. And unlike it s Act iveX count erpart , a Form s cont rol List box will updat e it s cont ent s in response t o changes t o a dynam ic nam ed range t hat has been assigned t o it s I nput range propert y. I t is a sim ple m at t er t o assign m ult iple Form s cont rols t o run t he sam e VBA procedure. Doing t he sam e wit h Act iveX cont rols requires a m ore com plicat ed class- based approach. I f you use m ult iple windows or t he split - panes feat ure in your applicat ion t o show t wo different views of t he sam e worksheet , Act iveX cont rols will only work in t he original window. Form s cont rols will work in any window.
Advantages of ActiveX Controls You can m odify t he appearance of Act iveX cont rols t o a m uch great er degree t han Form s cont rols. There are m ore variet ies of Act iveX cont rols t han t here are Form s cont rols. Act iveX cont rols have a wide variet y of event procedures t hat you can respond t o, whereas Form s cont rols can only run a single m acro.
N OTE I f you use Act iveX cont rols on worksheet s in Excel 97, you will frequent ly run int o problem s wit h VBA code t hat will not run in response t o a cont rol being select ed. This is because t he select ed cont rol has " st olen" t he focus from t he worksheet . Wit h t he except ion of t he Com m andBut t on cont rol, which has a TakeFocusOnClick propert y t hat can be set t o False t o elim inat e t his problem , t he first line of code associat ed wit h an Act iveX cont rol on a worksheet under Excel 97 should select a cell on t he worksheet t o ret urn t he focus t o it .
Practical Example I n t his sect ion we begin building a real- world Excel applicat ion t hat illust rat es t he point s m ade in t he chapt er t ext . Our applicat ion will be a t im e- t racking syst em t hat will st art as a sim ple, no- frills t im esheet and work it s way up t o being a full- feat ured Excel applicat ion as we progress t hrough t he book. Due t o space const raint s, we do not show every det ail involved in creat ing t his applicat ion. We dem onst rat e t he m aj or feat ures and allow you t o exam ine t he rest by perusing t he finished sam ple of t he applicat ion t hat is available on t he accom panying CD. This t im esheet applicat ion will hencefort h be referred t o by it s acronym PETRAS, which st ands for Professional Excel Tim esheet Report ing and Analysis Syst em . The first version of PETRAS will be a sim ple workbook cont aining a t im e- ent ry t able on one worksheet and dat a- validat ion list s on a second hidden worksheet . The user will be expect ed t o com plet e t he t im e- ent ry t able each week and m anually copy t he workbook t o a cent ral locat ion for consolidat ion. You can find t his version of PETRAS on t he accom panying CD in t he \ Applicat ion\ Ch04- Worksheet Design\ folder. I t is displayed in Figure 4- 33.
Figu r e 4 - 3 3 . Th e Fir st Ve r sion of t h e PETRAS Applica t ion [View full size image]
Most of t he user int erface design t echniques t hat have been discussed in t his chapt er have been used in t he PETRAS applicat ion, including all variat ions of defined nam es, st yles t o different iat e
areas by purpose, t able form at t ing t echniques, use of com m ent s for help t ext , dat a validat ion and condit ional form at t ing. Let 's quickly cover exam ples of how each of t hese t echniques is used in pr act ice.
Hidden Rows and Columns We've t aken advant age of hidden rows and colum ns in t he PETRAS applicat ion for t wo purposes: error checking and background dat a processing. Figure 4- 34 shows an open version of t he PETRAS user int erface workbook.
Figu r e 4 - 3 4 . Th e PETRAS Applica t ion w it h a ll Row s a n d Colu m n s Visible [View full size image]
This worksheet has t wo t ypes of hidden colum ns. The t wo init ial hidden colum ns are what we called program colum ns early in t he chapt er. We also have t wo hidden colum ns in t he m iddle of t he user int erface. These t wo colum ns are used t o creat e a dat a t able t hat m akes t he process of aut om at ically consolidat ing dat a sim pler, while not requiring t he user t o ent er duplicat e dat a for each row. As you'll see in Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins, we will have special- purpose code t hat uses t he set HideCols nam ed range, shown in t he first row, t o ensure t hese colum ns are hidden.
Defined Names The Tot al Hours colum n in Figure 4- 33 is calculat ed using a nam ed form ula called forTimeDiff.
We used a defined form ula for t his purpose because t he logic required is com plex and t herefore it m akes sense t o encapsulat e it . The forTimeDiff nam ed form ula m akes use of relat ive defined nam es t o reference each part of t he row from which it needs t o gat her t he dat a required t o perform it s calculat ion. List ing 4- 2 shows t his defined form ula.
List in g 4 - 2 . Th e forTimeDiff N a m e d For m u la =IF(COUNTA(inpEntryRow)inpStart, inpStop-inpStart, (1+inpStop)-inpStart ) )
The input - t ype defined nam es ( t hose wit h t he inp prefix) are all row- relat ive defined nam es t hat refer t o fixed colum ns on t he Tim eEnt ry worksheet , as follows: inpEnt ryRow = Tim eEnt ry! $F3: $K3 inpSt art = Tim eEnt ry! $J3 inpSt op = Tim eEnt ry! $K3 I f t here are fewer t han six ent ries in t he current row, t he form ula sim ply ret urns an em pt y st ring. We cannot allow t ot al hours t o be calculat ed for a row t hat has not been com plet ed. Aft er all of t he ent ries in a row have been com plet ed, we m ust com pare t he st art and st op t im es. These t im es are ent ered as Excel dat e serial t im e values; t herefore t hey are decim al values less t han or equal t o 1 t hat have no indicat ion of t he dat e worked. We set up t he t im esheet in t his m anner as a convenience t o t he user. I t allows t he user t o sim ply ent er a st art t im e and a st op t im e wit hout also having t o ent er a specific dat e for each t im e. I f t he st op t im e is great er t han t he st art t im e we know bot h ent ries refer t o t he sam e day. We can t hen j ust subt ract t he st art t im e from t he st op t im e t o calculat e t he num ber of hours worked. I f t he st op t im e is less t han or equal t o t he st art t im e, we know t he user began working prior t o m idnight on one day and finished working aft er m idnight on t he next day. I n t his case, we add 1 t o t he st op t im e, which is equivalent t o adding one day in t he Excel dat e serial form at , t o force it t o be great er t han t he st art t im e. We t hen subt ract t he st art t im e from t he result . This enables us t o account for sit uat ions in which users work past m idnight .
Styles Not e t hat PETRAS uses t he sam e st yles we int roduced in Figure 4- 8 . We use separat e st yles t o ident ify row and colum n headers, input areas, form ula result s and areas t hat are out side t he user int erface. The Tim eEnt ry worksheet in Figure 4- 33 is designed t o be prot ect ed, and once prot ect ed, t he only cells t hat can be m odified by t he user are cells having t he I nput st yle ( t he st yle wit h t he whit e background) .
User Interface Drawing Techniques The PETRAS applicat ion dem onst rat es t wo of our recom m ended user int erface drawing t echniques. As shown in Figure 4- 33, we've used borders t o give t he t im e- ent ry t able a 3D appearance and a sim ulat ed grid t o help guide t he user. We've also provided cell com m ent s t o answer t he m ost com m on quest ions t he user m ay have about t he user int erface. Figure 4- 35 shows t he cell com m ent describing t he Day colum n.
Figu r e 4 - 3 5 . A Ce ll Com m e n t Use d a s H e lp Te x t
Data Validation Dat a validat ion has been used in every input cell in t he PETRAS user int erface. Most of t he dat a validat ion derives from dynam ic list s st ored on t he hidden wksProgram Dat a worksheet , part of which is shown in Figure 4- 36.
Figu r e 4 - 3 6 . Th e H idde n w k sPr ogr a m D a t a W or k sh e e t [View full size image]
The Consult ant s colum n on t he wksProgram Dat a worksheet provides t he dat a- validat ion list for t he Consult ant ent ry on t he Tim eEnt ry worksheet . Sim ilarly, t he Act ivit ies colum n on t he wksProgram Dat a worksheet provides t he dat a- validat ion list for t he Act ivit y colum n on t he Tim eEnt ry worksheet and so on. A com plet e pict ure of t he various dat a validat ion t echniques used on t he Tim eEnt ry worksheet can be gained by exam ining t he sam ple applicat ion. Not e t hat a m ore com plex exam ple of t he cascading list s dat a- validat ion t echnique described earlier in t his chapt er is used t o connect t he Client and Proj ect colum ns on t he Tim eEnt ry worksheet .
Conditional Formatting I n Figure 4- 33, you can see t hat condit ional form at t ing has been used t o provide a clear visual indicat ion of t im e ent ries t hat were m ade on a weekend. This is because work done on weekend days t ypically cost s a higher hourly rat e t han work done on a weekday. Condit ional form at t ing is also used t o call out t he error condit ion creat ed when t he user changes t he first colum n ent ry of a cascading validat ion list pair if t he second ent ry has already been m ade. I n Figure 4- 37 below, t he user has m ist akenly changed a Client ent ry t o a client t hat does not m at ch t he Proj ect ent ry previously m ade. Not e how condit ional form at t ing m akes it inst ant ly recognizable t hat changing t he client ent ry was t he wrong t hing t o do in t his case.
Figu r e 4 - 3 7 . Con dit ion a l For m a t t in g N ot ifie s t h e Use r of a D a t a - En t r y Er r or [View full size image]
Conclusion We've discussed m any user- int erface building t echniques in t his chapt er; all of t hem im plem ent ed using Excel's built - in feat ures. However, don't lose sight of t he fact t hat t he m ost im port ant t hing about an Excel user int erface is not how m any cool t echniques you've used. Users don't care about cool t echniques. They want an int uit ive user int erface t hat m akes it easy for t hem t o get t heir j ob done. I deally your user int erface should not draw at t ent ion t o it self at all. I t should j ust be so well designed and const ruct ed t hat users can dive right in and st art working wit hout having t o spend any significant t im e figuring t hings out . Adhering t o t he eight principles described at t he beginning of t his chapt er can help you design user int erfaces t hat do t heir j ob so well no one not ices t hem . This is t he best user int erface design of all.
Chapter 5. Function, General and Application-Specific Add-ins Add- ins are t he prim ary const it uent s of m ost nont rivial Excel applicat ions. This chapt er discusses t he m ost im port ant dut ies add- ins perform wit hin an Excel applicat ion, as well as det ails about t he act ivit ies various t ypes of add- in m ust perform . This chapt er does not cover dict at or applicat ions, as t hat is t he subj ect of t he next chapt er.
The Four Stages of an Application Every applicat ion has four dist inct st ages, regardless of t he t ype of add- in used t o im plem ent it . These st ages are developm ent / m aint enance, st art up, runt im e and shut down. This sect ion briefly discusses all four st ages and ident ifies t he act ivit ies and services t hat m ust be provided by t he applicat ion during each st age. Som e of t he t opics covered do not apply t o all t ypes of applicat ions, but we cover t hem all t o give you a com plet e high- level overview of what an applicat ion m ust accom plish. We do not go int o great det ail on t he t opics covered in t his sect ion. Som e will be obvious t o readers of t his book and ot hers are covered ext ensively eit her lat er in t his chapt er or in lat er chapt ers of t he book.
Development/Maintenance During t his st age you are eit her writ ing t he applicat ion's code for t he first t im e or updat ing exist ing code in t he applicat ion. Eit her way, t he purpose of t his st age is t o build or fix t he applicat ion rat her t han t o run it . You can m ake your life easier during t his st age by using VBA code t o help build and m aint ain t he applicat ion you are writ ing. There are t wo m aj or cat egories of code t hat helps t o build code: Code t e m pla t e s These can be as sim ple as basic subrout ine and funct ion fram eworks m anually copied from a st ored m odule, or as com plex as t hird- part y code- generat ion t ools. The Excel Visual Basic Edit or ( VBE) provides a very rudim ent ary t ool for creat ing t em plat e subrout ines, funct ions and propert y procedures t hrough t he I nsert > Procedure m enu. D e ve lopm e n t u t ilit ie s You should st rive t o aut om at e as m any rout ine developm ent processes as possible. Your applicat ion should cont ain a dedicat ed code m odule, or even a separat e ut ilit y applicat ion, for VBA ut ilit ies t hat assist you in creat ing and m aint aining t he applicat ion. I n t he A Table- Driven Approach t o UI Worksheet Managem ent sect ion lat er in t he chapt er, we dem onst rat e a ut ilit y for aut om at ically m anaging t he set t ings on your user int erface worksheet s.
Startup When your applicat ion is st art ing up, it m ust perform a num ber of t asks depending on what t ype of applicat ion it is and t he condit ions it finds during t he st art up process. Ch e ck t h e e n vir on m e n t Check any environm ent al condit ions t hat m ust be sat isfied in order for your applicat ion t o run. This m ight include verifying t hat t he appropriat e version of Windows and Excel are inst alled as well as verifying t he exist ence of any addit ional program s and files your applicat ion depends on. I f t he st art up check fails, you can exit gracefully wit h a clear error m essage t o t he user rat her t han allowing your applicat ion t o cont inue unt il it encount ers a runt im e error.
Sa ve a ll se t t in gs t h a t m u st be r e st or e d on e x it I f your applicat ion m odifies t he user's Excel environm ent , it m ust save t he original set t ings so t hey can be rest ored prior t o exit ing. This t opic is covered ext ensively in Chapt er 6 Dict at or Applicat ions. Bu ild a n y dyn a m ic u se r in t e r fa ce e le m e n t s These include applicat ion- specific com m and bars, Excel Applicat ion- level set t ings, workbook t em plat es and so fort h. Re gist e r a n y u se r - de fin e d fu n ct ion s I f your add- in cont ains user- defined funct ions ( UDFs) you want t o expose t o t he user, you need t o add som e basic inform at ion about t hem t o t he Excel Funct ion Wizard. We cover t his t opic in t he Funct ion Library Add- ins sect ion lat er in t he chapt er . Se t t h e in it ia l u se r in t e r fa ce con figu r a t ion The specific set t ings m ade will depend on t he t ype of add- in and t he condit ions discovered at st art up. For exam ple, if t here were an applicat ion workbook open t hat belonged t o your applicat ion when t he add- in was opened, you would enable all of your applicat ion's m enu bars and t oolbars. Ot herwise you would probably disable m ost of t hem . This t ype of dynam ic com m and bar m odificat ion is covered in t he Pract ical Exam ple sect ion of Chapt er 7 Using Class Modules t o Creat e Obj ect s.
Runtime Runt im e is t he st age during which your applicat ion is perform ing t he operat ions t hat const it ut e it s prim ary purpose. H a n dle r e qu e st s fr om t h e u se r These include calls generat ed by com m and bar cont rols, Form s cont rols on worksheet s, Act iveX cont rols on userform s and worksheet s and any keyboard short cut s your applicat ion has provided for t he user. H a n dle Ex ce l a pplica t ion e ve n t s During runt im e your applicat ion m ust also be prepared t o respond t o ( and in som e cases suppress) event s generat ed by Excel it self. Excel applicat ion event handling is covered ext ensively in Chapt er 7 Using Class Modules t o Creat e Obj ect s. H a n dle r u n t im e e r r or s Alt hough we would like our applicat ions t o run flawlessly all t he t im e, every applicat ion event ually encount ers a runt im e error. These errors cannot be allowed t o st op your applicat ion dead in it s t racks. I nst ead, t hey m ust be handled gracefully and in such a way t hat t he user has som e idea of what went wrong. Error handling is covered ext ensively in Chapt er 12 VBA Error Handling . Ca ll code loca t e d in ot h e r a dd- in s I f you have set a reference t o anot her add- in using t he Tools > References m enu in t he VBE during developm ent , you can call public procedures locat ed in st andard m odules in t he referenced add- in direct ly by nam e. Wit hout references, you can accom plish t he sam e t hing by using t he Application.Run funct ion. Pr ovide ot h e r se r vice s Add- ins also provide ot her services at runt im e, t he m ost com m on being UDFs. We cover UDFs in det ail in t he Funct ion Library Add- ins sect ion lat er in t his chapt er .
Shutdown
The shut down st age is when your applicat ion is exit ing, eit her norm ally at t he request of t he user or abnorm ally as t he result of an error condit ion. Eit her way t here are act ivit ies t hat m ust be perform ed at t his st age. Un r e gist e r a n y u se r - de fin e d fu n ct ion s I f your add- in regist ered any UDFs wit h t he Excel Funct ion Wizard on st art up, it should unregist er t hese funct ions on shut down. Re m ove a ll a pplica t ion - spe cific u se r in t e r fa ce com pon e n t s This m eans rem oving all t he applicat ion- specific it em s creat ed during t he st art up phase ( com m and bars, applicat ionspecific workbooks and so fort h) . Re st or e t h e or igin a l e n vir on m e n t I f your applicat ion m ade any persist ent changes t o t he Excel environm ent , it m ust save t he original set t ings on st art up and rest ore t hem on shut down. This process is generically known as saving and rest oring t he user's workspace. This t opic is covered ext ensively in Chapt er 6 Dict at or Applicat ions.
Function Library Add-ins I t is quit e com m on t o encount er VBA add- ins t hat serve no ot her purpose t han t o provide a library of UDFs. These add- ins are called funct ion library add- ins. Add- ins are t he best cont ainer for host ing VBA user- defined funct ions because as long as t he user has t he add- in cont aining t he funct ions open, t hose funct ions will be available t o all current ly open workbooks. UDFs locat ed in a specific workbook are only available t o worksheet s in t hat workbook. Funct ion library add- ins are t he sim plest t ype of add- in from t he perspect ive of t he operat ional t asks t hey m ust accom plish. Alt hough t he funct ions it cont ains m ay be quit e com plex, t he funct ion library add- in it self has only t wo responsibilit ies: regist ering it s UDFs wit h t he Excel Funct ion Wizard on st art up and unregist ering t hem on shut down. I n t his sect ion, we first creat e a sam ple UDF and t hen show t he opt ions available t o t he add- in for handling it s regist rat ion dut ies.
An Example UDF A com m on sit uat ion encount ered when creat ing worksheet m odels is t he need t o use a com binat ion of t he I F and I SERROR worksheet funct ions t o t est t he result of anot her funct ion for an error condit ion. I f t he funct ion being t est ed evaluat es t o an error value, you const ruct t he I F funct ion t o ret urn som e default value in it s place. I f t he funct ion being t est ed does not evaluat e t o an error value, you const ruct t he I F funct ion t o execut e t he funct ion being evaluat ed a second t im e and ret urn it s result . When t he funct ion being t est ed is very long and/ or com plex, t he result ing form ula is doubly long and/ or com plex because you m ust evaluat e t he funct ion being t est ed t wice. This sit uat ion can be generalized by t he following pseudo- form ula:
=IF(ISERROR(),,)
I n t his sect ion, we writ e a UDF t hat perform s t his operat ion wit h j ust one pass of t he funct ion being evaluat ed. We'll call our UDF I FERROR and it s synt ax will be t he following:
=IFERROR(,)
List ing 5- 1 shows t he VBA code required t o im plem ent our I FERROR funct ion.
List in g 5 - 1 . Th e I FERROR Use r - D e fin e d Fu n ct ion
Public Function IFERROR(ByRef ToEvaluate As Variant, _ ByRef Default As Variant) As Variant If IsError(ToEvaluate) Then IFERROR = Default Else IFERROR = ToEvaluate End If End Function
The ToEvaluate argum ent is a value, cell reference or direct ly ent ered funct ion expression t o be evaluat ed. I f ToEvaluate cont ains an error value, t he Default argum ent is ret urned, ot herwise ToEvaluate is ret urned. The Default argum ent can also be a value, cell reference or direct ly ent ered expression. Bot h argum ent s and t he funct ion ret urn value of t he I FERROR funct ion are specified as Variant dat a t ypes in order t o provide t he m axim um flexibilit y in t he t ypes of argum ent s t he funct ion can accept and ret urn. As discussed in Chapt er 3 Excel and VBA Developm ent Best Pract ices, t he Variant dat a t ype can have a negat ive im pact on perform ance. I f you know, for exam ple, t hat you will always be passing cell references t o bot h I FERROR argum ent s, you can significant ly im prove t he perform ance of t he funct ion by changing t he dat a t ype of it s argum ent s and ret urn value t o t he Range dat a t ype.
UDF Naming Conventions Cust om worksheet funct ions and t heir argum ent s ( if any) should be given reasonably short descript ive nam es. You should do your best t o m ake your UDFs look and feel like built - in Excel worksheet funct ions. Therefore, you should not apply t he nam ing convent ions described in Chapt er 3 Excel and VBA Developm ent Best Pract ices t o UDFs.
Making Your UDF Appear Native You can m ake your UDFs appear m ore like nat ive Excel funct ions by regist ering t hem wit h t he Excel Funct ion Wizard. This involves giving t hem descript ions and assigning t hem t o cat egories t hat will assist t he user in figuring out how t o use t hem . There are t wo ways t o do t his; t he first is sim ple but lim it ed, t he second com plex but com plet e. The first way is t o use t he Application.MacroOptions m et hod. The m aj or advant ages of t he Application.MacroOptions m et hod are t he relat ively lengt hy funct ion descript ion allowed and t he fact t hat it rem oves your UDF from t he default User Defined cat egory when it places it under t he cat egory you specify. The disadvant ages of t his m et hod are t hat t he funct ion descript ion and cat egory are t he only opt ions you can specify and you cannot creat e cat egories t hat don't already exist in t he Funct ion Wizard. List ing 5- 2 shows an Aut o_Open procedure t hat uses t he Application.MacroOptions m et hod t o regist er our I FERROR funct ion.
List in g 5 - 2 . Re gist e r in g a UD F w it h Applica t ion .M a cr oOpt ion s
Sub Auto_Open() Dim sDescription As String sDescription = "Provides a short-cut replacement " & _ "for the common worksheet function construct:" & _ vbLf & "=IF(ISERROR(),,)" Application.MacroOptions Macro:="IFERROR", _ Description:=sDescription, _ Category:=9 End Sub
Excel's funct ion cat egories are specified by num eric values t hat correspond t o t he posit ion of t he cat egory in t he Excel Funct ion Wizard cat egory list , where All = 0, Financial = 1 and so on. I f you do not specify a cat egory num ber, your UDF will be assigned t o cat egory 14, User Defined, by default . I f you specify a cat egory num ber t hat does not exist , a runt im e error will occur. Table 5- 1 shows t he full set of available cat egory num bers, along wit h t heir corresponding cat egory nam es. Not all of t hese cat egories are com m only used.
Ta ble 5 - 1 . UD F Fu n ct ion Ca t e gor y N u m be r s a n d N a m e s Ca t e gor y N um ber
Ca t e gor y N a m e
0
All
1
Financial
2
Dat e & Tim e
3
Mat h & Trig
4
St at ist ical
5
Lookup & Reference
6
Dat abase
7
Text
8
Logical
9
I nfor m at ion
10
Com m ands
11
Cust om izing
12
Macro Cont rol
Ca t e gor y N um ber
Ca t e gor y N a m e
13
DDE/ Ex t er nal
14
User Defined
List ing 5- 3 shows an Aut o_Close procedure t hat uses t he Application.MacroOptions m et hod t o unregist er our I FERROR funct ion. The only drawback t o t his m et hod is it leaves your funct ion nam e list ed in t he All cat egory wit h no descript ion.
List in g 5 - 3 . Un r e gist e r in g a UD F w it h Applica t ion .M a cr oOpt ion s Private Sub Auto_Close() Application.MacroOptions Macro:="IFERROR", _ Description:=Empty, _ Category:=Empty End Sub
Keep in m ind t hat t he Application.MacroOption exam ples shown above regist er and unregist er a single funct ion. I f your add- in cont ains m ult iple UDFs, you will need t o add one call t o t he Application.MacroOptions m et hod for each UDF in bot h t he Aut o_Open and t he Aut o_Close pr ocedur e. The second way t o provide descript ions for your UDFs requires you t o execut e an XLM m acro funct ion t o bot h regist er and unregist er t hem . XLM is t he nat ive Excel program m ing language t hat predat es VBA but is st ill support ed in Excel. More t han t en years aft er it supposedly becam e obsolet e, t here are st ill t hings t hat XLM does bet t er t han VBA, and t his is a good exam ple. The advant age of t his m et hod is it gives you com plet e cont rol over all aspect s of t he descript ion and cat egorizat ion of your funct ion. The disadvant age of t his m et hod is t he XLM m acro st ring, which m ust cont ain all nam es, descript ions and ot her inform at ion, is lim it ed t o 255 charact ers in lengt h. This m eans your descript ions m ust be kept short . Your UDF will also cont inue t o appear in t he default User Defined cat egory even t hough it also appears in any new cat egory t hat you specify. Credit for originally devising t his t echnique goes t o worksheet funct ion expert Laurent Longre. The code required t o dem onst rat e t his m et hod will not fit wit hin t he lim it ed confines of a print ed page, so you will need t o exam ine t he MRegist er m odule in t he Funct ion.xls workbook locat ed on t he CD in t he Concept s folder for t his chapt er t o see how it works. The code in t his m odule is com m ent ed ext ensively t o help you underst and it and is designed so t he ent ire m odule can be copied int o anot her proj ect and work correct ly. You will j ust need t o m odify t he funct ion descript ions and add room for addit ional funct ions t o suit your needs. One procedure call placed in your add- in's Aut o_Open procedure will regist er all of your UDFs and one procedure call placed in your add- in's Aut o_Close procedure will unregist er all of your UDFs.
N OTE I f you do not do anyt hing t o prevent it , any public funct ion in your add- in will aut om at ically be list ed in t he User Defined cat egory in t he Excel Funct ion Wizard. This will occur even if your funct ion isn't designed t o be used as a worksheet funct ion at all. The solut ion t o t his problem is t o add t he Option Private Module direct ive t o t he t op of any m odule t hat cont ains public funct ions. This will not prevent public worksheet funct ions from being used as such, but it will prevent t hem from being aut om at ically added t o t he User Defined funct ions cat egory.
Creating a Friendly Name and Description for Your Function Library Addin Funct ion library add- ins are t ypically placed in t he add- ins list of t he Excel Add- ins dialog accessed t hrough t he Tools > Add- ins m enu. This enables t he user t o easily load and unload t hem as t he need arises. We discuss t he various ways of m aking your add- in appear in t he Tools > Add- ins dialog in Chapt er 24 Providing Help, Securing, Packaging and Dist ribut ing. You should provide a friendly nam e and a short descript ion for any add- in t hat appears in t he Add- ins dialog. These can be m ade t o appear when t he add- in's ent ry is select ed by set t ing t wo specific file propert ies of t he add- in workbook. First , set t he I sAddin propert y of t he add- in's ThisWorkbook obj ect t o False so you can access t he add- in workbook from t he Excel user int erface. Then choose File > Propert ies from t he Excel m enu. On t he Sum m ar y t ab of t he result ing Propert ies dialog, you provide a friendly nam e for your add- in using t he Tit le ent ry. The descript ion for your add- in is ent ered in t he Com m ent s field. Figure 5- 1 shows t he nam e and descript ion for our sam ple funct ion library add- in.
Figu r e 5 - 1 . Addin g a N a m e a n d D e scr ipt ion t o a n Add- in
Figure 5- 2 shows how t he nam e and descript ion added in t he Propert ies dialog appear in t he Addins dialog.
Figu r e 5 - 2 . Th e Add- in N a m e a n d D e scr ipt ion in t h e Add- in s D ia log
Critical UDF Details The following are som e crit ical det ails you need t o underst and in order t o develop VBA user- defined funct ions. One of t he m ost com m on m ist aken beliefs about UDFs is t hat t hey can change t he value of cells ot her t han t he one t hey've been ent ered int o. This is not t he case. Whet her a UDF is program m ed in a VBA add- in ( described in t his chapt er) a VB6 aut om at ion add- in ( described in Chapt er 21 Writ ing Add- ins wit h Visual Basic 6) or a C/ C+ + XLL ( described in Chapt er 19 XLLs and t he C API ) , a user- defined funct ion can only m odify t he value of t he cell int o which it has been ent ered. A UDF cannot change any propert ies of t he cell it has been ent ered int o ot her t han t he value of t hat cell. At t em pt ing t o set t he pat t ern or borders of a cell from wit hin a UDF, for exam ple, will not work, alt hough neit her does it cause a VBA or funct ion ret urn value error. UDFs m ust be locat ed in st andard code m odules. They cannot be placed in class m odules, userform s or t he code m odules behind workbooks or worksheet s. Do not at t em pt t o change t he values of UDF argum ent s t hat are declared ByRef. As we discuss in a m om ent , UDFs can only change t he value of t he cell int o which t hey have been ent ered. At t em pt ing t o m odify t he value of a ByRef argum ent violat es t his principle and will cause your UDF t o ret urn a # VALUE! error if you at t em pt t o m odify a ByRef argum ent t hat has been declared as a Range obj ect . Put all range references used by your funct ion in t he funct ion's argum ent list . I f you refer t o ranges not in t he argum ent list from wit hin your UDF, changes t o t hose ranges will not cause your UDF t o recalculat e, possibly leading t o incorrect result s being displayed. You can use t he
Application.Volatile m et hod t o force your UDF t o recalculat e whenever calculat ion is t riggered anywhere in Excel, but t his can cause serious degradat ion of calculat ion perform ance and som e t ypes of worksheet event handling and should be used only as a last r esor t .
VBA UDF Problems The biggest problem wit h m ost VBA UDFs is t hat using t hem creat es hard- coded links from t he workbooks where t hey are used t o t he add- in t hat cont ains t hem . I f you physically m ove t he source add- in, workbooks t hat use t he funct ions it cont ains will not be able t o locat e it , even if t he add- in is opened prior t o opening any workbooks t hat use it s funct ions. I f you use t he XLM UDF regist rat ion m et hod described in t he sect ion on Creat ing a Friendly Nam e and Descript ion for Your Funct ion Library Add- in, you can avoid t his problem . VBA funct ions also do not aut om at ically capit alize correct ly when you ent er t hem int o a worksheet cell. Capit alizat ion does not affect t he operat ion of t he funct ion, but it gives t he funct ion a st range appearance. And aft er you have ent ered a UDF in all lowercase charact ers, you will find it alm ost im possible t o get it t o convert t o uppercase charact ers even when it is defined t hat way in t he source code in t he add- in.
TI P When writ ing any worksheet funct ion for Excel 97, always use an error handler. An unhandled error t hat occurs inside a UDF in Excel 97 for any reason will cause m any different problem s depending on how t he funct ion calculat ion was t riggered. The worst of t hese problem s will halt your VBA program in it s t racks if t he calculat ion was t riggered by VBA code.
General Add-ins General add- ins, also known as ut ilit y add- ins, are designed t o enhance Excel by ext ending it s feat ure set or providing connect ions bet ween it and ot her program s. All Excel ut ilit y add- ins fall int o t his cat egory, including m ost of t he add- ins you will find in t he Tools folder of t he CD t hat accom panies t his book. Like funct ion library add- ins, general add- ins are designed t o work wit h any arbit rary workbook t he user opens. General add- ins are t ypically cont ained wit hin a single .xla workbook, alt hough larger exam ples m ay be dist ribut ed across m ult iple files. General add- ins are t ypically placed in t he add- ins list of t he Excel Add- ins dialog so t he user can easily load and unload t hem as t he need arises. They are provided wit h a friendly nam e and descript ion in exact ly t he sam e way as described for a funct ion library add- in in t he previous sect ion. The m ost com m on m et hod for providing t he user access t o feat ures in a general add- in is t hrough a m enu and/ or t oolbar. Event handling is also frequent ly used t o respond t o user act ions, while keyboard short cut s can be provided t o run com m only used feat ures. Because general add- ins m ust operat e correct ly no m at t er what st at e Excel is current ly in, any event handling class you use m ust respond t o changes in t he Excel environm ent t hat would prevent your add- in from operat ing correct ly. An exam ple would be t he user closing t he last open workbook. I n response t o t his sit uat ion, your event handler should disable access t o t he feat ures of your add- in t hat require an open workbook t o funct ion correct ly. Event handling is covered in m ore det ail in Chapt er 7 Using Class Modules t o Creat e Obj ect s. The individual ent ry- point procedures of your add- in m ust also verify t hat t he current st at e of Excel is valid for t hem t o run. For exam ple, if a procedure is designed t o operat e on a chart , but t here is no chart current ly act ive, your procedure m ust det ect t his and provide t he user wit h an explanat ory error m essage rat her t han falling over wit h a runt im e error caused by an at t em pt t o reference a nonexist ent chart .
Application-Specific Add-ins Applicat ion- specific add- ins differ from t he previous t wo add- in t ypes in t hat t hey are designed t o im plem ent self- cont ained Excel applicat ions. They m ay int egrat e wit h t he st andard Excel user int erface, as does t he add- in shown in t he Pract ical Exam ple sect ion lat er in t he chapt er, or t hey m ay t ake over t he Excel user int erface ent irely, as dem onst rat ed in Chapt er 6 Dict at or Applicat ions. I n eit her case, an applicat ion- specific add- in is designed t o operat e only on workbooks t hat have been specifically designed for it . St ill, m ost of t he sam e operat ions and requirem ent s apply t o applicat ion- specific add- ins as apply t o funct ion libraries and general add- ins. Applicat ion specific add- ins sim ply add an addit ional elem ent , t he worksheet user int erface.
A Table-Driven Approach to UI Worksheet Management A significant part of t he responsibilit y of an applicat ion- specific add- in is t o m anage t he user int erface workbook and worksheet set t ings. I t 's possible t o st ore, apply and rem ove t hese set t ings on an ad hoc basis, but when dealing wit h large and/ or com plex user int erfaces it is m uch bet t er t o let VBA do t he work for you. I n t his sect ion, we dem onst rat e a t able- driven approach t o m anaging worksheet user int erface set t ings. Typically, a num ber of set t ings m ust be m ade prior t o your applicat ion being run by t he end user. However, t hese set t ings will get in your way during developm ent . The solut ion is t o creat e an aut om at ed syst em for defining, applying and rem oving t hese set t ings. This is j ust one of m any areas in Excel developm ent t hat lends it self t o a t able- driven m et hodology.
Table-Driven Methodology Defined I n a nut shell, t able- driven m et hods use a worksheet t able t o st ore dat a t hat describes t he t ask you are t rying t o accom plish. One or m ore VBA procedures read t he dat a from t he t able and aut om at ically perform t hat t ask. The biggest advant age of a t able- driven m et hod for accom plishing a specific t ask is it t ends t o be easily reusable from proj ect t o proj ect . Com m on t asks t hat lend t hem selves t o t able- driven solut ions are m anaging workbook and worksheet user int erface set up, building com m and bars and saving and rest oring t he user's workspace. We even dem onst rat e a t able- driven m et hod for creat ing userform s in Chapt er 10 Userform Design and Best Pract ices.
Typical Worksheet User Interface Settings A num ber of set t ings or configurat ion det ails t end t o be com m on t o all worksheet user int erfaces, including t he following
H idde n r ow s a n d colu m n s As discussed in Chapt er 4 Worksheet Design, having hidden rows and colum ns at your disposal is a valuable user int erface const ruct ion t echnique. However, you don't want t hese rows and colum ns t o be hidden when you are perform ing developm ent or m aint enance work on t he user int erface. Pr ot e ct ion Workbook and worksheet prot ect ion are fundam ent al t o good user int erface design. Prot ect ing t he user int erface prevent s users from m odifying areas of t he user int erface t hat should not be m odified. Scr oll a r e a Set t ing a scroll area for each user int erface worksheet prevent s users from get t ing lost by prevent ing t hem from scrolling beyond t he area used by your applicat ion. En a ble se le ct ion This propert y works wit h t he scroll area propert y t o keep t he user focused on your user int erface. I t prevent s t he user from even select ing cells t hat are out side t he boundaries of your UI . Row a n d colu m n h e a de r s Alt hough t here are som e except ions, you t ypically do not want t o display Excel's row and colum n headers on your user int erface worksheet s. Row and colum n headers serve as guidelines for const ruct ing and m aint aining a user int erface, so t hey should be visible during t hat process. I n m ost well- designed user int erfaces, however, t hey will j ust be a dist ract ion t o t he user. Therefore, you want t hem visible during developm ent and hidden at runt im e. Sh e e t visibilit y I n m ost nont rivial workbook- based user int erfaces, you will have one or m ore worksheet s t hat are designed t o perform background t asks t hat should not be seen or m odified by t he user. Once again, however, you want t hese sheet s t o be visible during developm ent and m aint enance.
The Settings Table Let 's see how a t able- driven m et hodology can help us creat e and m anipulat e t he user int erface set t ings described above. The first t hing we need is a set t ings t able. This t able will list t he nam es of t he user int erface worksheet s in t he first colum n and t he nam es of t he set t ings in t he first row. At t he int ersect ion of each row and colum n is t he value t hat will be applied t o a specific worksheet for t hat set t ing. Figure 5- 3 shows an exam ple of a user int erface set t ings t able.
Figu r e 5 - 3 . A Use r I n t e r fa ce Se t t in gs Ta ble [View full size image]
This t able would t ypically be locat ed on a worksheet in your add- in. The VBA code used t o m anage t hese set t ings would be locat ed in a ut ilit y m odule in t he add- in. A ut ilit y m odule is a st andard code m odule t hat holds code designed t o assist t he program m er during developm ent and not used by t he applicat ion it self. I n sit uat ions where add- in size needs t o be m inim ized, t his t able and it s associat ed code could be locat ed in a separat e ut ilit y workbook t hat is only used during developm ent and m aint enance. However, you will st ill need t o have som e user int erface m anagem ent code locat ed in t he add- in t o set t he propert ies of your user int erface when your applicat ion is run. There are t wo t hings t o not e about t his t able. First , you can see t hat all of t he worksheet nam es in t he first colum n follow our worksheet nam ing convent ion. This is because t hey are t he CodeNam es of t he worksheet s, not t heir sheet t ab nam es. As m ent ioned in Chapt er 3 Excel and VBA Developm ent Best Pract ices, you should never rely on sheet t ab nam es because t hey m ay change frequent ly or unexpect edly. A worksheet 's CodeNam e provides a perm anent , unique nam e by which you can ident ify t he worksheet . Second, beginning in colum n B you can see t hat t he colum n headers are defined nam es ( rem em ber t hat t he set prefix ident ifies a defined nam e t hat represent s a set t ing) . This is because all of t hese set t ings will be st ored as sheet - level defined nam es on t he worksheet s t o which t hey apply. This allows all of t he inform at ion required t o m anage a worksheet t o be encapsulat ed wit hin t hat w or k sheet . Also not ice t hat som e of t he set t ings in t he body of t he t able are blank. This indicat es t he set t ing does not apply t o t hat worksheet and t he set t ing nam e will not be defined on t hat worksheet . Lat er you will see t hat t he VBA code t hat im plem ent s t hese set t ings ignores set t ings whose defined nam es are m issing.
The Utility Code The VBA code t hat im plem ent s t he t able- driven set t ings needs t o accom plish t wo obj ect ives:
1 . I t needs t o read t he set t ings t able and add t he specified defined nam es wit h t he specified values t o each worksheet list ed in t he t able.
2 . I t needs t o loop each worksheet in t he user int erface workbook, read t he value of each defined nam e list ed in t he set t ings t able and record t he value of t hat set t ing in t he appropriat e cell of t he set t ings t able. These t asks will be im plem ent ed as t wo separat e procedures. I f we have code t o creat e t he set t ings defined in t he set t ings t able, you m ay be asking yourself, why do we also need code t o read t hese set t ings back int o t he t able? Good quest ion. The answer is t hat when you are working direct ly on t he user int erface workbook you will oft en find it easier t o m anually updat e t he value of a defined nam e for a set t ing you've j ust alt ered. For exam ple, if you need t o add addit ional hidden program colum ns t o several sheet s, it 's very easy t o updat e t heir respect ive set ProgCols defined nam es as you go along. Aft er you've m ade t hese adj ust m ent s, you can quickly synchronize t he set t ings t able and t he user int erface workbook by reading t he values of all t he defined nam es from t he workbook back int o t he set t ings t able. As you can see in List ing 5- 4 , t he code required t o apply t he set t ings in t he set t ings t able t o t he worksheet s in t he user int erface workbook is relat ively sim ple. The defined nam es t hat reference t he list of worksheet s in t he first colum n and t he list of set t ings in t he first row are bot h dynam ic. I f you add worksheet s or set t ings, t hese nam es aut om at ically expand t o include t hem . Keep in m ind t hat t his code does not act ually a pply any set t ings in t he user int erface workbook. I t sim ply records t he set t ings we want t o apply on each worksheet using worksheet - level defined nam es. A procedure in t he applicat ion t hat runs as part of t he st art up code will read t hese defined nam es and apply t he set t ings t hey specify.
List in g 5 - 4 . Code t o W r it e Se t t in gs t o t h e Use r I n t e r fa ce W or k sh e e t s Private Const msFILE_TEMPLATE As String = "PetrasTemplate.xlt" Private Const msRNG_NAME_LIST As String = "tblRangeNames" Private Const msRNG_SHEET_LIST As String = "tblSheetNames" Public Sub WriteSettings() Dim rngSheet As Range Dim rngSheetList As Range Dim rngName As Range Dim rngNameList As Range Dim rngSetting As Range Dim sSheetTab As String Dim wkbTemplate As Workbook Dim wksSheet As Worksheet ' Turning off screen updating and calculation ' will speed the process significantly. Application.ScreenUpdating = False Application.Calculation = xlCalculationManual ' The time entry workbook. Set wkbTemplate = Application.Workbooks(msFILE_TEMPLATE) ' The list of worksheets in the first column. Set rngSheetList = wksUISettings.Range(msRNG_SHEET_LIST)
' The list of setting names in the first row. Set rngNameList = wksUISettings.Range(msRNG_NAME_LIST) ' The outer loop processes all the worksheets in the ' first column of the table. For Each rngSheet In rngSheetList ' We need an object reference to the worksheet so we ' can easily add a sheet-level defined name to it. ' The sSheetTabName() function converts a CodeName ' into its corresponding sheet tab name. sSheetTab = sSheetTabName(wkbTemplate, rngSheet.Value) Set wksSheet = wkbTemplate.Worksheets(sSheetTab) ' The inner loop adds each setting to the current sheet. ' If the setting already exists it will be replaced. For Each rngName In rngNameList ' The value of the setting is contained in the cell ' where the worksheet row and range name column ' intersect. Set rngSetting = Intersect(rngSheet.EntireRow, _ rngName.EntireColumn) ' We only create defined names for settings that ' have been given a non-zero-length value. If Len(rngSetting.Value) > 0 Then wksSheet.Names.Add rngName.Value, _ "=" & rngSetting.Value End If Next rngName Next rngSheet Application.ScreenUpdating = True Application.Calculation = xlCalculationAutomatic End Sub
The sam ple add- in discussed in t he Pract ical Exam ple sect ion lat er in t his chapt er cont ains code t hat aut om at ically applies t hese set t ings t o t he user int erface workbook when it is first opened. The MUt ilit y m odule of t he applicat ion also cont ains a procedure t hat aut om at ically rem oves all t hese set t ings from t he user int erface workbook t o m ake it easier t o m aint ain.
Using VBA to Dynamically Modify Your Worksheet User Interface You can leverage t he power of VBA t o im prove your user int erface in m any ways. Many of t hem , including t echniques such as cont ext - specific com m and bar enabling and dynam ic hiding and
unhiding of rows and colum ns, require t he use of Excel event t rapping, which we cover in Chapt er 7 Using Class Modules t o Creat e Obj ect s. A sim ple exam ple t hat we'll add t o our sam ple applicat ion is a feat ure t hat clears t he dat a- ent ry cells on a worksheet . A one- click m et hod for clearing all input cells on t he current user int erface worksheet is oft en very helpful t o users. To do t his, j ust creat e a nam ed range t hat includes all t he dat a- input cells on each dat a- ent ry worksheet and give it an obvious nam e such as rgnClearI nput s. This m ust be a sheet - level defined nam e creat ed on all dat a- ent ry worksheet s in your workbook. List ing 5- 5 shows t he VBA im plem ent at ion of our clear dat a- ent ry area feat ure.
List in g 5 - 5 . VBA I m ple m e n t a t ion of a Cle a r D a t a - En t r y Ar e a Fe a t u r e Public Sub ClearDataEntryAreas() Dim rngToClear As Range ' Make sure the active worksheet has the rgnClearInputs ' defined name (i.e. it's an input worksheet). On Error Resume Next Set rngToClear = ActiveSheet.Range("rgnClearInputs") On Error GoTo 0 ' If the worksheet is an input worksheet, clear the ' contents of the input area. If Not rngToClear Is Nothing Then rngToClear.ClearContents End Sub
Practical Example Features The add- in for our PETRAS t im esheet applicat ion will perform t he following operat ions: Open and init ialize t he applicat ion Build a t oolbar t hat gives t he user access t o each feat ure of t he applicat ion Open and init ialize t he t im e- ent ry workbook Enable t he user t o save a copy of t he t im e ent ry workbook t o a predefined consolidat ion locat ion Enable t he user t o add m ore dat a- ent ry rows t o t he t im e- ent ry worksheet Enable t he user t o clear t he dat a- ent ry area so t he t im esheet can easily be reused Enable t he user t o close t he PETRAS applicat ion Add a cust om propert y t hat will allow t he consolidat ion applicat ion t o locat e all inst ances of our t im e- ent ry workbook Let 's look at how t he add- in accom plishes t hese t asks. We'll assum e t he Writ eSet t ings ut ilit y procedure shown in List ing 5- 4 has been run on t he t im e- ent ry workbook and t he set t ings saved prior t o running t he add- in.
Open and Initialize the Application The first operat ion t he add- in perform s when it is opened is t o init ialize t he applicat ion and t hen open and init ialize t he user int erface workbook. This is accom plished by t he Aut o_Open procedure, shown in List ing 5- 6 .
List in g 5 - 6 . Th e PETRAS Add- in Au t o_ Ope n Pr oce du r e Public Sub Auto_Open() Dim wkbBook As Workbook ' The very first thing your application should do upon
' startup is attempt to delete any copies of its ' command bars that may have been left hanging around ' by an Excel crash or other incomplete exit. On Error Resume Next Application.CommandBars(gsBAR_TOOLBAR).Delete On Error GoTo 0 ' Initialize global variables. InitGlobals ' Make sure we can locate our time entry workbook before ' we do anything else. If Len(Dir$(gsAppDir & gsFILE_TIME_ENTRY)) > 0 Then Application.ScreenUpdating = False Application.StatusBar = gsSTATUS_LOADING_APP ' Build the command bars. BuildCommandBars ' Determine if the time entry workbook is already ' open. If not, open it. If so, activate it. On Error Resume Next Set wkbBook = Application.Workbooks(gsFILE_TIME_ENTRY) On Error GoTo 0 If wkbBook Is Nothing Then Set wkbBook = Application.Workbooks.Open( _ gsAppDir & gsFILE_TIME_ENTRY) Else wkbBook.Activate End If ' Make the worksheet settings for the time entry ' workbook MakeWorksheetSettings wkbBook ' Reset critical application properties. ResetAppProperties Else MsgBox gsERR_FILE_NOT_FOUND, vbCritical, gsAPP_NAME ShutdownApplication End If End Sub
The first t hing t he add- in does is blindly at t em pt t o delet e any previous inst ance of it s t oolbar. This should be considered a best pract ice. Applicat ion t oolbars can be left behind due t o an incom plet e shut down, which will t hen cause an error when you t ry t o creat e t hem again t he next t im e your applicat ion is run. Then t he add- in init ializes any global variables. I n t his case we have t wo: a
variable t hat holds t he full pat h where t he add- in is locat ed and a variable t hat indicat es when t he add- in is in t he process of shut t ing down. As m ent ioned in Chapt er 3 Excel and VBA Developm ent Best Pract ices, you should use as few global variables as possible. When you do use t hem , you m ust m ake sure t hey are in a known st at e at t he beginning of every procedure where t hey m ight be accessed. Encapsulat ing t his logic in an I nit Globals procedure t hat can be called wherever it 's needed is a good way t o m anage t his pr ocess. Aft er t he add- in has perform ed t hese t wo basic t asks, it checks t o see whet her it can locat e t he user int erface workbook. I f t he user int erface workbook is locat ed, execut ion cont inues. Ot herwise, an error m essage displays and t he applicat ion exit s. This m akes sense because t here is not hing t he add- in can do wit hout t he user int erface workbook.
Build a Toolbar That Gives the User Access to Each Feature Next t he add- in builds it s t oolbar. The t echniques used t o do t his are basic, hard- coded VBA com m and bar building t echniques t hat should be fam iliar t o all readers of t his book. Therefore, we do not go int o any det ail on t his. An ent ire chapt er is devot ed t o advanced com m and bar building t echniques, Chapt er 8 Advanced Com m and Bar Handling. The add- in exposes four dist inct feat ures for t he user t hrough t he applicat ion t oolbar, as shown in Figure 5- 4 . Each of t hese feat ures is discussed in t he sect ions t hat follow.
Figu r e 5 - 4 . Th e PETRAS Applica t ion Toolba r
Open and Initialize the Time-Entry Workbook Aft er t he com m and bars have been const ruct ed, t he add- in checks t o see whet her t he user int erface workbook is already open. I f t his workbook is not open, t he Auto_Open procedure opens it . I f t his workbook is open, t he Aut o_Open procedure act ivat es it . The next st ep is t o init ialize t he user int erface workbook. During t his process, all t he set t ings t hat were saved t o t he user int erface workbook's worksheet s by t he Writ eSet t ings procedure in List ing 5- 4 are read and applied by t he MakeWorksheet Set t ings procedure. List ing 5- 7 shows t his procedure.
List in g 5 - 7 . Th e M a k e W or k sh e e t Se t t in gs Pr oce du r e Public Sub MakeWorksheetSettings(ByRef wkbBook As Workbook)
Dim Dim Dim Dim Dim Dim
rngCell As Range rngSettingList As Range rngHideCols As Range sTabName As String vSetting As Variant wksSheet As Worksheet
Set rngSettingList = wksUISettings.Range(gsRNG_NAME_LIST) For Each wksSheet In wkbBook.Worksheets ' The worksheet must be unprotected and visible in order ' to make many of the settings. It will be protected and ' hidden again automatically by the settings code if it ' needs to be protected and/or hidden. wksSheet.Unprotect wksSheet.Visible = xlSheetVisible ' Hide any non-standard columns that need hiding. Set rngHideCols = Nothing On Error Resume Next Set rngHideCols = wksSheet.Range(gsRNG_SET_HIDE_COLS) On Error GoTo 0 If Not rngHideCols Is Nothing Then rngHideCols.EntireColumn.Hidden = True End If For Each rngCell In rngSettingList ' Determine if the current worksheet requires the ' current setting. vSetting = Empty On Error Resume Next If rngCell.Value = "setScrollArea" Then ' The scroll area setting must be treated ' differently because it's a range object. Set vSetting = Application.Evaluate( _ "'" & wksSheet.Name & "'!" & rngCell.Value) Else vSetting = Application.Evaluate( _ "'" & wksSheet.Name & "'!" & rngCell.Value) End If On Error GoTo 0 If Not IsEmpty(vSetting) Then If rngCell.Value = "setProgRows" Then If vSetting > 0 Then wksSheet.Range("A1").Resize(vSetting) _ .EntireRow.Hidden = True End If ElseIf rngCell.Value = "setProgCols" Then If vSetting > 0 Then
wksSheet.Range("A1").Resize(, _ vSetting).EntireColumn.Hidden = True End If ElseIf rngCell.Value = "setScrollArea" Then wksSheet.ScrollArea = vSetting.Address ElseIf rngCell.Value = "setEnableSelect" Then wksSheet.EnableSelection = vSetting ElseIf rngCell.Value = "setRowColHeaders" Then wksSheet.Activate Application.ActiveWindow _ .DisplayHeadings = vSetting ElseIf rngCell.Value = "setVisible" Then wksSheet.Visible = vSetting ElseIf rngCell.Value = "setProtect" Then If vSetting Then wksSheet.Protect , True, True, True End If End If End If Next rngCell Next wksSheet ' Leave the Time Entry worksheet active. sTabName = sSheetTabName(wkbBook, gsSHEET_TIME_ENTRY) wkbBook.Worksheets(sTabName).Activate End Sub
The MakeWorksheet Set t ings procedure loops t hrough all t he worksheet s in t he specified workbook and applies all t he set t ings t hat we have defined for each worksheet . We have designed t his procedure t o accept a reference t o a specific workbook obj ect as an argum ent rat her t han having it assum ing it needs t o operat e on t he user int erface workbook because t his design will allow us t o generalize t he applicat ion t o handle m ult iple user int erface workbooks if we need t o at som e lat er t im e. The set t ings t able on which t his procedure is based can be seen on t he wksUI Set t ings worksheet of t he Pet rasAddin.xla workbook. Aft er t he user int erface workbook has been init ialized, t he last t hing we do is run a procedure t hat ensures all Excel applicat ion propert ies are set t o t heir default values. This is t he Reset AppPropert ies procedure shown in List ing 5- 8 .
List in g 5 - 8 . Th e Re se t AppPr ope r t ie s Pr oce du r e Public Sub ResetAppProperties() Application.StatusBar = False Application.ScreenUpdating = True Application.DisplayAlerts = True
Application.EnableEvents = True Application.EnableCancelKey = xlInterrupt Application.Cursor = xlDefault End Sub
This procedure is useful because we can m ake what ever applicat ion set t ings we like during t he code execut ion required for a feat ure, and as long as we call t his procedure before we exit we know t hat all crit ical applicat ion propert ies will be left in known good st at es. I f we didn't happen t o use one of t he propert ies reset by t his procedure, it doesn't m at t er. The values set by t he Reset AppPropert ies procedure are t he default values for each propert y. Therefore we aren't changing t hem if t hey weren't used.
Save a Copy of the Time-Entry Workbook to a Predefined Consolidation Location The first t oolbar but t on will save a copy of t he t im e- ent ry workbook t o a cent ralized consolidat ion locat ion. From here, a procedure in t he PETRAS report ing applicat ion will consolidat e t he t im eent ry workbooks from all t he consult ant s int o a single report . List ing 5- 9 shows t he procedure t hat im plem ent s t his feat ure.
List in g 5 - 9 . Th e Post Tim e En t r ie sToN e t w or k Pr oce du r e Public Sub PostTimeEntriesToNetwork() Dim Dim Dim Dim Dim Dim Dim Dim
sSheetTab As String sWeekEndDate As String sEmployee As String sSaveName As String sSavePath As String wksSheet As Worksheet wkbBook As Workbook vFullName As Variant
' Don't do anything unless our time entry workbook is active ' wkbBook will return a reference to it if it is. If bIsTimeEntryBookActive(wkbBook) Then ' Make sure the TimeEntry worksheet does not have any ' data entry errors. sSheetTab = sSheetTabName(wkbBook, gsSHEET_TIME_ENTRY) Set wksSheet = wkbBook.Worksheets(sSheetTab) If wksSheet.Range(gsRNG_HAS_ERRORS).Value Then MsgBox gsERR_DATA_ENTRY, vbCritical, gsAPP_NAME Exit Sub End If ' Create a unique name for the time entry workbook.
sWeekEndDate = Format$( _ wksSheet.Range(gsRNG_WEEK_END_DATE).Value, _ "YYYYMMDD") sEmployee = wksSheet.Range(gsRNG_EMPLOYEE_NAME).Value sSaveName = sWeekEndDate & " - " & sEmployee & ".xls" ' Check the registry to determine if we already have a ' consolidation path specified. If so, save the time ' entry workbook to that location. If not, prompt the ' user to identify a consolidation location, save that ' location to the registry and save the time entry ' workbook to that location. sSavePath = GetSetting(gsREG_APP, gsREG_SECTION, _ gsREG_KEY, "") If Len(sSavePath) = 0 Then ' No path was stored in the registry. Prompt the ' user for one. vFullName = Application.GetOpenFilename( _ Title:=gsCAPTION_SELECT_FOLDER) If vFullName False Then ' NOTE: The InStrRev function was not available ' in Excel 97. sSavePath = Left$(vFullName, _ InStrRev(vFullName, "\")) SaveSetting gsREG_APP, gsREG_SECTION, _ gsREG_KEY, sSavePath Else ' The user cancelled the dialog. MsgBox gsMSG_POST_FAIL, vbCritical, gsAPP_NAME Exit Sub End If End If wkbBook.SaveCopyAs sSavePath & sSaveName MsgBox gsMSG_POST_SUCCESS, vbInformation, gsAPP_NAME Else MsgBox gsMSG_BOOK_NOT_ACTIVE, vbExclamation, gsAPP_NAME End If End Sub
This procedure shows t he safet y m echanism we use t o prevent runt im e errors from occurring if t he user clicks one of our t oolbar but t ons wit hout t he user int erface workbook being act ive. Prior t o perform ing any act ion, we verify t hat t his workbook is act ive using t he bI sTim eEnt ryBookAct ive (wkbBook) funct ion call. This funct ion ret urns an obj ect reference t o t he t im e- ent ry workbook via it s ByRef Workbook argum ent if t he t im e- ent ry workbook is act ive. I f t he t im e- ent ry workbook is not act ive, we display an error m essage t o t he user and exit . Aft er we verify t he t im e- ent ry workbook is act ive, we check t he error flag in t he hidden colum n on
t he t im e- ent ry worksheet t o det erm ine whet her t he t im esheet has any dat a- ent ry errors. I f t he flag indicat es errors, we display a m essage t o t he user and exit . I f t here are no dat a- ent ry errors, t he next t ask is t o creat e a unique nam e for t he workbook and look for our consolidat ion pat h in t he regist ry. I f t he consolidat ion pat h has not yet been saved t o t he regist ry, we prom pt t he user t o specify t he pat h t hat should be used. Finally, we use t he SaveCopyAs m et hod of t he Workbook obj ect t o post a copy of t he workbook t o t he cent ral consolidat ion locat ion. We t hen display a m essage t o t he user indicat ing t hat t he process succeeded.
Allow the User to Add More Data-Entry Rows to the Time-Entry Worksheet I n t he version of t he t im e- ent ry workbook dem onst rat ed in Chapt er 4 Worksheet Design, t he num ber of dat a- ent ry rows was fixed. I n t his version, t he second t oolbar but t on will enable t he user t o add addit ional rows t o t he t im e- ent ry t able as needed. List ing 5- 10 shows t he procedure t hat im plem ent s t his feat ure.
List in g 5 - 1 0 . Th e AddM or e Row s Pr oce du r e Public Sub AddMoreRows() Const lOFFSET_COLS As Long = 5 Const lINPUT_COLS As Long = 6 Dim rngInsert As Range Dim wkbBook As Workbook Dim wksSheet As Worksheet ' Don't do anything unless our time entry workbook is active If bIsTimeEntryBookActive(wkbBook) Then ' Get a reference to the TimeEntry worksheet and the ' insert row range on it. All new rows will be inserted ' above this range. Set wksSheet = wkbBook.Worksheets(sSheetTabName( _ wkbBook, gsSHEET_TIME_ENTRY)) Set rngInsert = wksSheet.Range(gsRNG_INSERT_ROW) ' Add a new row to the time entry table. wksSheet.Unprotect rngInsert.EntireRow.Insert rngInsert.Offset(-2, 0).EntireRow.Copy _ Destination:=rngInsert.Offset(-1, 0) rngInsert.Offset(-1, lOFFSET_COLS) _ .Resize(1, lINPUT_COLS).ClearContents wksSheet.Protect , True, True, True Else
MsgBox gsMSG_BOOK_NOT_ACTIVE, vbExclamation, gsAPP_NAME End If End Sub
I n t he AddMoreRows procedure, we use t he sam e m et hod t o det erm ine whet her a t im e- ent ry workbook is act ive as we used in t he Post Tim eEnt riesToNet work procedure. Aft er we've det erm ined we have a valid workbook act ive, insert ing a new row is a t hree- st ep process: 1 . I nsert a new row direct ly above t he last row in t he t able. The last row in t he t able is m arked by t he gsRNG_I NSERT_ROW defined nam e. 2 . Copy t he row above t he newly insert ed row and past e it ont o t he newly insert ed row. This ensures all funct ions, form at t ing and validat ion required t o m ake t he t able operat e and appear correct ly are t ransferred t o t he newly insert ed row. 3 . The cont ent s of t he dat a- ent ry area of t he newly insert ed row is cleared of any dat a t hat m ay have been t ransferred t o t he new row by t he previous st ep. The new dat a- ent ry row is now clean and ready t o be used.
Allow the User to Clear the Data-Entry Area So the Timesheet Can Be Reused The t hird t oolbar but t on, Clear Dat a Ent ries, sim ply clears t he values from all t he dat a- ent ry areas on t he t im esheet . The code t o im plem ent t his feat ure was discussed in t he Using VBA t o Dynam ically Modify Your Worksheet User I nt erface sect ion above, so we don't repeat it here.
Allow the User to Close the PETRAS Application The fourt h and last t oolbar but t on sim ply closes t he PETRAS applicat ion workbooks and rem oves it s t oolbar. The Exit Applicat ion procedure t hat im plem ent s t his feat ure is shown in List ing 5- 11.
List in g 5 - 1 1 . Th e Ex it Applica t ion Pr oce du r e Public Sub ExitApplication() ShutdownApplication End Sub
This is a one- line st ub procedure t hat j ust calls t he Shut downApplicat ion procedure, which act ually perform s t he t asks required t o shut down t he applicat ion. We place t he shut down logic in a separat e procedure because it m ust be called from t he Exit Applicat ion procedure as well as from t he Aut o_Close procedure. These t wo procedures reflect t he t wo ways t he user could exit our
applicat ion: select ing t he Exit PETRAS t oolbar but t on or using one of Excel's built - in exit feat ures. List ing 5- 12 shows t he code for t he Shut downApplicat ion procedure.
List in g 5 - 1 2 . Th e Sh u t dow n Applica t ion Pr oce du r e Public Sub ShutdownApplication() ' Blow past any errors on application shutdown. On Error Resume Next ' This flag prevents this procedure from being called a ' second time by Auto_Close if it has already been called ' by the ExitApplication procedure. gbShutdownInProgress = True ' Delete command bar. Application.CommandBars(gsBAR_TOOLBAR).Delete ' Close the time entry workbook, allowing the user to ' save changes. Application.Workbooks(gsFILE_TIME_ENTRY).Close ' If there are no workbooks left open, quit Excel ' Otherwise just close this workbook. If lCountVisibleWorkbooks() = 0 Then ThisWorkbook.Saved = True Application.Quit Else ThisWorkbook.Close False End If End Sub
The Shut downApplicat ion procedure is an exam ple of a procedure where you want t o ignore any errors. The applicat ion is closing down, so t here isn't anyt hing useful t hat could be done about any errors t hat did occur. Therefore, we t ell VBA t o ignore any errors in t he procedure by using t he On Error Resume Next st at em ent . Chapt er 12 VBA Error Handling covers t his st at em ent in det ail. The first t hing t he Shut downApplicat ion procedure does is set a global flag variable t hat will prevent it from being called t wice if t he user init iat ed shut down by clicking t he Exit PETRAS t oolbar but t on. The process of closing t he add- in workbook will cause t he Aut o_Close procedure t o fire. The Aut o_Close procedure also calls Shut downApplicat ion, but it checks t he value of t he gbShut downI nProgress variable first and sim ply exit s if shut down is already in progress. Then t he Shut downApplicat ion procedure delet es t he applicat ion t oolbar. I t t hen closes t he user's t im e- ent ry workbook. I f t his workbook has not been saved, we allow Excel t o prom pt t he user t o save t he workbook. Aft er t he t im e- ent ry workbook has been closed, we check t o see whet her any ot her v isib le workbooks are open. I f no visible workbooks are open, we can assum e t he user
st art ed Excel j ust t o run our applicat ion and t herefore we can close Excel. I f t here are st ill visible workbooks open, we assum e t he user was working wit h Excel before our applicat ion was opened and t herefore we j ust close our add- in and leave Excel open for t he user t o cont inue working wit h. The v isib le workbooks dist inct ion is an im port ant one because m any users have a hidden Personal.xls workbook or ot her ut ilit y workbook always open. We want t o ignore t hese hidden workbooks when t rying t o det erm ine whet her we should close Excel or leave Excel open on exit . List ing 5- 13 shows t he procedure t hat count s t he num ber of visible workbooks.
List in g 5 - 1 3 . Th e lCou n t Visible W or k book s Pr oce du r e Public Function lCountVisibleWorkbooks() As Long Dim lCount As Long Dim wkbBook As Workbook For Each wkbBook In Application.Workbooks If wkbBook.Windows(1).Visible Then lCount = lCount + 1 End If Next wkbBook lCountVisibleWorkbooks = lCount End Function
Add a Custom Property to Allow the Consolidation Application to Locate All Instances of Our Time-Entry Workbook Aft er all em ployees have saved t heir t im e- ent ry workbooks t o t he cent ralized consolidat ion locat ion, t he consolidat ion applicat ion needs t o be able t o definit ively locat e t hese workbooks. There m ay be ot her files locat ed in t he consolidat ion direct ory t hat t he consolidat ion applicat ion needs t o ignore. We solve t his problem by adding a cust om docum ent propert y called Pet rasTim esheet t o our t im e- ent ry workbook. This allows t he consolidat ion applicat ion t o uniquely ident ify any t im e- ent ry workbooks creat ed by our applicat ion. To add a cust om docum ent propert y, act ivat e t he Pet rasTem plat e.xls workbook and choose File > Pr oper t ies from t he Excel m enu. I n t he Propert ies dialog, select t he Cust om t ab. Ent er Pet rasTim esheet in t he Nam e box and ent er Yes in t he Value box. Click t he Ad d but t on t o add t his propert y t o t he workbook. Figure 5- 5 shows t he result .
Figu r e 5 - 5 . Addin g t h e Cu st om D ocu m e n t Pr ope r t y
Application Organization We briefly cover t he way in which t he PETRAS add- in has been organized int o code m odules. The Pet rasAddin.xla workbook is a very sim ple, ent irely procedural based applicat ion at t his point . I t consist s of six st andard code m odules whose nam es provide a reasonable indicat ion of t he t ype of code t hey cont ain. These m odules are t he following: M En t r y Poin t s This m odule cont ains t he procedures called from t he t oolbar but t ons of our t oolbar. These procedures are ent ry point s in t he sense t hat t hey are t he only way for t he user t o execut e code in t he applicat ion. The ClearDat aEnt ryAreas procedure shown in List ing 5- 5 , t he Post Tim eEnt riesToNet work procedure shown in List ing 5- 9 , t he AddMoreRows procedure shown in List ing 5- 10 and t he Exit Applicat ion procedure shown in List ing 5- 11 are all locat ed in t his m odule.
M Globa ls This m odule cont ains t he definit ions of all public const ant s and variables used by our applicat ion as well as an I nit Globals procedure used t o ensure our global variables are always properly init ialized. M Ope n Close This m odule cont ains t he code required t o st art up and shut down t he applicat ion. The Aut o_Open procedure shown in List ing 5- 6 is locat ed in t his m odule. M St a n da r dCode This m odule cont ains st andard code library procedures t hat are reused wit hout m odificat ion in m any different proj ect s. The Reset AppPropert ies procedure shown in List ing 5- 8 is one exam ple. M Sy st e m Code This m odule cont ains core procedures writ t en specifically for t his applicat ion. I n a larger applicat ion, you would have m any m odules of t his t ype, each of which would have a m ore det ailed descript ive nam e ( for exam ple, MPrint ing, MCalculat ion or MExport ) . M Ut ilit ie s This m odule cont ains procedures designed solely for use by t he program m er during const ruct ion and m aint enance of t he applicat ion. Procedures in t his m odule will never be run by t he end user and in fact are hidden from t he user by t he Option Private Module direct ive. The Writ eSet t ings procedure shown in List ing 5- 4 is locat ed in t his m odule.
Conclusion This chapt er has covered a com plet e list of st ages all Excel applicat ions go t hrough. I t has described t he various t ypes of add- ins and t he t ypes of operat ions t hat each is required t o perform . I t has dem onst rat ed a t able- driven m et hod for m aint aining and applying crit ical set t ings in a user int erface workbook and dem onst rat ed a sim ple exam ple of an applicat ion- specific add- in applicat ion.
Chapter 6. Dictator Applications Dict at or applicat ions t ake cont rol of t he ent ire Excel session, m odifying t he user int erface t o m ake it appear and behave like a norm al Windows program . By doing t his, t he applicat ions can leverage Excel's rich user int erface feat ures and/ or m ake ext ensive use of Excel's calculat ion engine and analysis feat ures, while m aint aining a high degree of cont rol over t he user. This chapt er explains how t o creat e a sim ple dict at or applicat ion, providing a basic t em plat e from which you can build your own. Lat er chapt ers in t his book add m ore feat ures t o t he basic applicat ion we present here.
Structure of a Dictator Application As m ent ioned in Chapt er 2 Applicat ion Archit ect ures, m ost dict at or applicat ions have t he following logical st ruct ure: A st art up rout ine t o perform version and dependency checks and so fort h A core set of rout ines, t o: Take a snapshot of t he Excel environm ent set t ings and t o rest ore t hose set t ings Configure and lock down t he Excel applicat ion Creat e and rem ove t he dict at or applicat ion's com m and bars Handle copying and past ing dat a wit hin t he worksheet t em plat es ( if using worksheet based dat a- ent ry form s) Provide a library of com m on helper rout ines and classes A backdrop worksheet , t o display wit hin t he Excel window while userform s are being shown, usually wit h som e form of applicat ion- specific logo ( if we're prim arily using form s for t he user int er face) Mult iple independent applet s, which provide t he applicat ion's funct ionalit y Mult iple t em plat e worksheet s used by t he applet s, such as dat a- ent ry form s or preform at t ed report t em plat es Each of t hese point s is discussed in m ore det ail below in t he order in which t hey occur wit hin a t ypical dict at or applicat ion. I n t he sim plest dict at or applicat ions, t hese elem ent s are all cont ained wit hin a single workbook, alt hough spreading t hem over m ult iple workbooks can m ake m aint enance easier when a t eam of developers works on a large applicat ion.
Startup and Shutdown Version and Dependency Checks All versions of Excel from 97 t o 2003 share t he sam e file form at , so if our applicat ion requires a m inim um version level ( for exam ple, Excel 2000 in our case) , we need t o check t hat our user hasn't j ust opened t he applicat ion in Excel 97. The easiest way t o do t his is t o check t he value of t he Application.Version propert y. The original version of Excel 97 was version 8.0, which increm ent ed t o 8.0e wit h t he various service packs. Each m aj or release of Excel increm ent s t he
version num ber, so Excel 2000 is version 9.0, Excel 2002 is version 10.0 and Excel 2003 is version 11.0. I n List ing 6- 1 , we check t hat t he user is running Excel 2000 or lat er.
List in g 6 - 1 . Ch e ck in g t h e Ex ce l Ve r sion 'Check that the version is at least Excel 2000 If Val(Application.Version) < 9 Then MsgBox "The PETRAS Reporting application " & _ "requires Excel 2000 or later.", _ vbOKOnly, gsAPP_TITLE ThisWorkbook.Close False Exit Sub End If
Aft er we know we're running in an appropriat e version of Excel, we have t o check t hat t he user has inst alled any ext ra com ponent s we require, such as t he Analysis Toolpak or Solver add- ins, or ot her applicat ions t hat we're aut om at ing, such as Word or Out look. For add- ins, we can eit her check t he Application.Addins collect ion, or check t hat t he file exist s based on t he Application.LibraryPath. To check t hat ot her applicat ions are inst alled, we can eit her look direct ly in t he regist ry ( using API calls) or use CreateObject t o t ry t o creat e a new inst ance of t he applicat ion and t est for failure. This is covered in m ore det ail in Chapt er 18 Cont rolling Ot her Office Applicat ions.
Storing and Restoring Excel Settings To t ake full cont rol of t he Excel session, dict at or applicat ions t ypically cust om ize t he int erface t o a high degree, such as hiding t he t oolbars and form ula bar and changing num erous applicat ion set t ings. Unfort unat ely ( and despit e repeat ed request s t o Microsoft ) , Excel assum es t hese changes are t he user's choice of set t ings and should be preserved for t he next session; t here's no way t o t ell Excel t hese are t em porary set t ings, for t his session only. To solve t his problem , we have t o t ake a snapshot of t he Excel set t ings when our applicat ion st art s, st ore t hem away som ewhere and reset t hem as part of our applicat ion's shut down processing. The easiest place t o st ore t he set t ings is in a worksheet in t he add- in, alt hough our preference is t o st ore t hem in t he regist ry, so t hey can be recovered if t he applicat ion crashes ( see below) . The biggest issue wit h using t he regist ry is if t he com pany's securit y policy is such t hat regist ry access is blocked. I n t hat case, Excel won't be able t o st ore any user set t ings, so it doesn't m at t er t hat we won't be able t o st ore/ rest ore t hem eit her. List ing 6- 2 shows a t ypical rout ine t o st ore t he Excel set t ings.
List in g 6 - 2 . St or in g Ex ce l Se t t in gs in t h e Re gist r y Public Const gsREG_APP As String = "Company\Application" Public Const gsREG_XL_ENV As String = "Excel Settings"
Sub StoreExcelSettings() Dim Dim Dim Dim
cbBar As CommandBar sBarNames As String objTemp As Object wkbTemp As Workbook
'Skip errors in case we can't use the registry On Error Resume Next 'Check if we've already stored the settings '(so don't want to overwrite them) If GetSetting(gsREG_APP, gsREG_XL_ENV, "Stored", "No") _ = "No" Then 'Some properties require a workbook open, so create one If ActiveWorkbook Is Nothing Then Set wkbTemp = Workbooks.Add End If 'Indicate that the settings have been stored. 'This key will be deleted in RestoreSettings. SaveSetting gsREG_APP, gsREG_XL_ENV, "Stored", "Yes" 'Store the current Excel settings in the registry With Application SaveSetting gsREG_APP, gsREG_XL_ENV, _ "DisplayStatusBar", CStr(.DisplayStatusBar) SaveSetting gsREG_APP, gsREG_XL_ENV, _ "DisplayFormulaBar", CStr(.DisplayFormulaBar) 'etc. 'Which commandbars are visible For Each cbBar In .CommandBars If cbBar.Visible Then sBarNames = sBarNames & "," & cbBar.Name End If Next SaveSetting gsREG_APP, gsREG_XL_ENV, _ "VisibleCommandBars", sBarNames 'Special items for Excel 2000 and up If Val(.Version) >= 9 Then SaveSetting gsREG_APP, gsREG_XL_ENV, _ "ShowWindowsInTaskbar", _ CStr(.ShowWindowsInTaskbar) End If 'Special items for Excel 2002 and up If Val(.Version) >= 10 Then Set objTemp = .CommandBars
SaveSetting gsREG_APP, gsREG_XL_ENV, _ "DisableAskAQuestion", _ CStr(objTemp.DisableAskAQuestionDropdown) SaveSetting gsREG_APP, gsREG_XL_ENV, _ "AutoRecover", CStr(.AutoRecover.Enabled) End If End With 'Close up the temporary workbook If Not wkbTemp Is Nothing Then wkbTemp.Close False End If End Sub
List ing 6- 3 shows t he corresponding rout ine t o rest ore t he set t ings, which should be called during t he applicat ion's shut down processing.
List in g 6 - 3 . Re st or in g Ex ce l Se t t in gs D u r in g Sh u t dow n Sub RestoreExcelSettings() Dim vBarName As Variant Dim objTemp As Object 'Restore the original Excel settings from the registry With Application 'Check that we have some settings to restore If GetSetting(gsREG_APP, gsREG_XL_ENV, "Stored", "No") _ = "Yes" Then .DisplayStatusBar = CBool(GetSetting(gsREG_APP, _ gsREG_XL_ENV, "DisplayStatusBar", _ CStr(.DisplayStatusBar))) .DisplayFormulaBar = CBool(GetSetting(gsREG_APP, _ gsREG_XL_ENV, "DisplayFormulaBar", _ CStr(.DisplayFormulaBar))) 'etc. 'Show the correct toolbars On Error Resume Next For Each vBarName In Split(GetSetting(gsREG_APP, _ gsREG_XL_ENV, "VisibleCommandBars"), ",")
Application.CommandBars(vBarName).Visible = True Next On Error GoTo 0 'Specific stuff for Excel 2000 and up If Val(.Version) >= 9 Then .ShowWindowsInTaskbar = CBool(GetSetting(gsREG_APP, _ gsREG_XL_ENV, "ShowWindowsInTaskbar", _ CStr(.ShowWindowsInTaskbar))) End If 'Specific stuff for Excel 2002 and up If Val(.Version) >= 10 Then Set objTemp = .CommandBars objTemp.DisableAskAQuestionDropdown = _ CBool(GetSetting(gsREG_APP, gsREG_XL_ENV, _ "DisableAskAQuestion", _ CStr(objTemp.DisableAskAQuestionDropdown))) .AutoRecover.Enabled = CBool(GetSetting(gsREG_APP, _ gsREG_XL_ENV, "AutoRecover", _ CStr(.AutoRecover.Enabled))) End If 'Once restored, delete all the registry entries DeleteSetting gsREG_APP, gsREG_XL_ENV End If End With 'Restore the Excel menus RestoreMenus
End Sub
Toolbar cust om izat ions are st ored in a file wit h an .xlb ext ension, where t he filenam e differs wit h each version of Excel. Each t im e a perm anent change is m ade t o t he t oolbars, inform at ion about t he change is added t o t he file. By t heir very nat ure, dict at or applicat ions usually m ake lot s of changes t o t he t oolbars, result ing in t he XLB file growing quit e rapidly ( alt hough it can be reduced by creat ing t he t oolbars wit h t he temporary param et er set t o True) . This result s in slowing Excel's st art up processing and event ually causes Excel t o crash at st art up. To avoid t his, t he best way t o rest ore t he user's t oolbar configurat ion is t o find and open t he XLB file j ust before t he applicat ion closes. By doing so, Excel doesn't see any changes, so t he XLB file isn't m odified. The Rest oreMenus rout ine t o do t his is shown in List ing 6- 4 .
List in g 6 - 4 . Re st or in g Ex ce l Toolba r s D u r in g Sh u t dow n
Public Const gsMENU_BAR As String = "PETRAS Menu Bar" Sub RestoreMenus() Dim Dim Dim Dim
cbCommandBar As CommandBar sPath As String sToolbarFile As String vBarName As Variant
On Error Resume Next 'Reopen the xlb toolbar customization file '(if it exists), to avoid it growing in size sPath = Application.StartupPath 'Work out the name of the correct toolbar file to open, 'depending on the version of Excel If Val(Application.Version) = 9 Then sToolbarFile = Left$(sPath, InStrRev(sPath, "\")) & _ "Excel.xlb" Else sToolbarFile = Left$(sPath, InStrRev(sPath, "\")) & _ "Excel" & Val(Application.Version) & ".xlb" End If 'If there is one, reopen the toolbar file If Dir(sToolbarFile) "" Then Workbooks.Open sToolbarFile, ReadOnly:=True Else 'If not, we have to tidy up ourselves 'Re-enable all the toolbars For Each cbCommandBar In Application.CommandBars cbCommandBar.Enabled = True Next 'Delete our Application's toolbar Application.CommandBars(gsMENU_BAR).Delete End If End Sub
Handling Crashes I t is an unfort unat e fact of Excel applicat ion developm ent t hat at som e point , Excel m ight crash while our applicat ion is being used. I f/ when t hat happens, our norm al shut down processing will not have t he chance t o run, so Excel will rest art wit h our applicat ion's set t ings inst ead of t he user's. I f we want , we can handle t his by copying t he Rest oreExcelSet t ings rout ine int o a new workbook, calling it from t he Workbook_Open procedure and saving it as anot her add- in t hat we dist ribut e
wit h our applicat ion. Our St oreExcelSet t ings rout ine can be m odified t o copy t he add- in t o t he Application.StartupPath and our Rest oreExcelSet t ings rout ine can be m odified t o delet e it . I n doing so, t he add- in will be left behind if Excel crashes and will be opened and run by Excel when it rest art s, reset t ing t he environm ent t o t he way t he user had it .
Configuring the Excel Environment Aft er we've t aken t he snapshot of t he user's environm ent set t ings, we can configure Excel t o suit our applicat ion, such as: Set t ing t he applicat ion capt ion and icon Hiding t he form ula bar and st at us bar Set t ing calculat ion t o m anual ( because recalcs will be under program cont rol) Set t ing Application.IgnoreRemoteRequests = True, so double- clicking a workbook in Explorer opens a new inst ance of Excel inst ead of reusing our inst ance Swit ching off Windows in TaskBar, because we're likely t o have m ult iple processing workbooks open t hat we don't want t he user t o be able t o swit ch t o Swit ching off t he Ask a Quest ion drop- down from t he com m and bars Prevent ing t he abilit y t o cust om ize t he com m and bars Swit ching off aut o- recover ( in Excel 2002 and lat er)
Supporting a Debug Mode When developing and debugging our dict at or applicat ion, we will need a m echanism t o enable us t o access t he VBE, hidden sheet s and so on and allow quick and easy swit ching bet ween Excel's int erface and our applicat ion's, yet prevent our users from doing t he sam e. A sim ple m et hod is t o check for t he exist ence of a specific file in a specific direct ory at st art up and set a global gbDebugMode Boolean variable accordingly. We can t hen configure t he Excel environm ent different ly for debug and product ion m odes. I n debug m ode, we'll keep all Excel's short cut keys act ive and set up an ext ra short cut t o swit ch back t o Excel's m enus ( by calling t he Rest oreExcelSet t ings rout ine from List ing 6- 4 ) . I n product ion m ode, we'll disable all Excel's short cut keys and ensure t he VBE window is hidden. List ing 6- 5 shows a t ypical rout ine t o configure t he Excel environm ent for a dict at or applicat ion. I f t est ing t his rout ine, we recom m end you do so wit h t he debug.ini file creat ed.
List in g 6 - 5 . Con figu r in g t h e Ex ce l En vir on m e n t for a D ict a t or Applica t ion Public gvaKeysToDisable As Variant Public gbDebugMode As Boolean
Sub InitGlobals() gvaKeysToDisable = Array("^{F6}", "+^{F6}", "^{TAB}", _ "+^{TAB}", "%{F11}", "%{F8}", "^W", "^{F4}", _ "{F11}", "%{F1}", "+{F11}", "+%{F1}", "^{F5}", _ "^{F9}", "^{F10}") 'Use the existence of a debug file to set whether we're 'in debug mode gbDebugMode = Dir(ThisWorkbook.Path & "\debug.ini") "" End Sub
Sub
ConfigureExcelEnvironment()
Dim objTemp As Object Dim vKey As Variant With Application 'Set the Application properties we want .Caption = gsAPP_TITLE .DisplayStatusBar = True .DisplayFormulaBar = False .Calculation = xlManual .DisplayAlerts = False .IgnoreRemoteRequests = True .DisplayAlerts = True .Iteration = True .MaxIterations = 100 'Specific items for Excel 2000 and up If Val(.Version) >= 9 Then .ShowWindowsInTaskbar = False End If 'Specific items for Excel 2002 and up If Val(.Version) >= 10 Then Set objTemp = .CommandBars objTemp.DisableAskAQuestionDropdown = True objTemp.DisableCustomize = True .AutoRecover.Enabled = False End If 'We'll have slightly different environment states, _ 'depending on whether we're debugging or not If gbDebugMode Then 'Since we have blitzed the environment, we should 'set a hot key combination to restore it. 'That key combination is Shift+Ctrl+R .OnKey "+^R", "RestoreExcelSettings"
Else 'Make sure the VBE isn't visible .VBE.MainWindow.Visible = False 'Disable a whole host of shortcut keys For Each vKey In gvaKeysToDisable .OnKey vKey, "" Next End If End With End Sub
Not e t hat t he init ial value of e ve r y persist ent environm ent propert y changed in t he configurat ion rout ine should be st ored at st art up and rest ored at shut down, so any ext ra propert ies you need t o change m ust be added t o all t hree rout ines. We're assum ing t he dict at or applicat ion shut s down Excel when it closes, so t here's no need t o st ore such t hings as t he applicat ion t it le and so fort h.
Customizing the User Interface Preparing a Backdrop Graphic At t his point , we have a locked- down em pt y screen, ready for us t o add our applicat ion's user int erface. The first UI elem ent t o add will t ypically be som e sort of background graphic t o display as our applicat ion's " deskt op." The sim plest version of t his is t o have a single worksheet cont ained in our applicat ion workbook t hat is copied t o a new, visible workbook. The workbook is t hen m axim ized, has t he appropriat e worksheet display at t ribut es set and t he display range is zoom ed t o fill t he Excel window, as shown in List ing 6- 6 . The workbook windows can t hen be prot ect ed t o rem ove t he cont rol box and m axim ize/ m inim ize but t ons:
List in g 6 - 6 . Code t o Pr e pa r e a Ba ck gr ou n d Gr a ph ic W or k book Public gwbkBackDrop As Workbook Public Const gsBACKDROP_TITLE As String = "BackdropWkbk" Sub PrepareBackDrop() Dim wkbBook As Workbook If Not WorkbookAlive(gwbkBackDrop) Then 'See if there's already a backdrop workbook out there Set gwbkBackDrop = Nothing For Each wkbBook In Workbooks
If wkbBook.BuiltinDocumentProperties("Title") = _ gsBACKDROP_TITLE Then Set gwbkBackDrop = wkbBook Exit For End If Next If gwbkBackDrop Is Nothing Then 'Copy the backdrop sheet out of this workbook 'into a new one for display wksBackdrop.Copy Set gwbkBackDrop = ActiveWorkbook gwbkBackDrop.BuiltinDocumentProperties("Title") = _ gsBACKDROP_TITLE End If End If With gwbkBackDrop .Activate 'Select the full region that encompasses the backdrop 'graphic, so we can use Zoom = True to size it to fit .Worksheets(1).Range("rgnBackDrop").Select 'Set the Window View options to hide everything With .Windows(1) .WindowState = xlMaximized .Caption = "" .DisplayHorizontalScrollBar = False .DisplayVerticalScrollBar = False .DisplayHeadings = False .DisplayWorkbookTabs = False 'Zoom the selected area to fit the screen .Zoom = True End With 'Prevent selection or editing of any cells With .Worksheets(1) .Range("ptrCursor").Select .ScrollArea = .Range("ptrCursor").Address .EnableSelection = xlNoSelection .Protect DrawingObjects:=True, _ UserInterfaceOnly:=True End With 'Protect 'control .Protect .Saved = End With
the backdrop workbook, to remove the menu Windows:=True True
End Sub
'Function to test if a given workbook object variable 'points to a valid workbook Function WorkbookAlive(wbkTest As Workbook) As Boolean On Error Resume Next If Not wbkTest Is Nothing Then WorkbookAlive = wbkTest.Sheets(1).Name "" End If End Function
A m ore com plex version will cont ain m ult iple pot ent ial backdrop sheet s, each designed for a specific screen resolut ion or window size. At runt im e, t he appropriat e sheet is select ed, based on t he window's height or widt h.
Sheet-Based vs. Form-Based User Interfaces There are t wo prim ary st yles of user int erface for dict at or applicat ions: t hose t hat use worksheet s for t he m ain dat a- ent ry form s and t hose t hat use userform s. Bot h st yles can be com bined wit h a cust om m enu st ruct ure, alt hough it is slight ly harder wit h a form - based user int erface. Worksheet - based user int erfaces are very sim ilar t o t he applicat ion- specific add- ins discussed in Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins and are designed t o m ake m axim um use of Excel's rich cell- edit ing feat ures, such as aut o- com plet e, dat a validat ion and condit ional form at t ing. Alt hough t he use of Excel's rich funct ionalit y is a com pelling choice, care m ust be t aken t o ensure t he users do not accident ally dest roy t he dat a- ent ry form . I f you decide on a worksheet based user int erface, use worksheet s for all your m aj or dat a- ent ry form s and report s; dialogs should only be used for m inor t asks and wizards. Form - based user int erfaces are t ypically found in applicat ions t hat use Excel prim arily for it s calculat ion and analysis feat ures, rat her t han t he rich edit ing experience. The dat a- ent ry form s t end t o be m uch sim pler t han t hose where a worksheet is used, which is oft en perceived as a benefit for bot h t he user and t he developer; t he reduced funct ionalit y and t ight er cont rol t hat userform s provide can result in less chance for your users t o m ake m ist akes and hence a m ore robust solut ion. I f you decide t o use a form - based user int erface, worksheet s should only be used for report ing. Designing a form - based user int erface is covered in det ail in Chapt er 10 Userform Design and Best Pract ices. Trying t o m ix t he t wo user int erface st yles rarely works well; it is j ust t oo cum bersom e t o m ake worksheet s behave like dialogs ( such as t abbing bet ween cont rols) and vice versa ( such as aut ocom plet e) , part icularly if t he worksheet also includes som e form s cont rols ( such as but t ons, check boxes and so on) . When deciding which st yle t o use, base t he decision on where users are likely t o spend t he m aj orit y of t heir t im e. Will it be bet t er ( for t he user) t o provide t he rich edit ing feat ures of a worksheet , or t he t ight er cont rol of a userform ?
Handling Cut, Copy and Paste The biggest issue wit h sheet - based user int erfaces is having t o override Excel's default handling of cut , copy, past e and drag/ drop. As discussed in Chapt er 4 Worksheet Design, m ost of t he edit able cells in a dat a- ent ry worksheet will be given specific st yles, dat a validat ion and condit ional form at s. Unfort unat ely, Excel's default copy/ past e behavior will overwrit e t he form at t ing of t he cell being past ed t o and Excel's default cut behavior is t o form at t he cell being cut wit h t he Norm al st yle ( which is usually used for t he sheet background) . Excel's drag/ drop feat ure is t he sam e as cut and past e and will also dest roy t he dat a- ent ry sheet if used. The only way t o avoid t his is t o swit ch off drag/ drop and code our own cut , copy and past e rout ines, such as t hose shown in List ing 6- 7 .
List in g 6 - 7 . Code t o H a n dle Cu t , Copy a n d Pa st e for D a t a - En t r y W or k sh e e t s Dim mbCut As Boolean Dim mrngSource As Range 'Initialise cell copy-paste Public Sub InitCutCopyPaste() 'Hook all the cut, copy and paste keystrokes Application.OnKey "^X", "DoCut" Application.OnKey "^x", "DoCut" Application.OnKey "+{DEL}", "DoCut" Application.OnKey "^C", "DoCopy" Application.OnKey "^c", "DoCopy" Application.OnKey "^{INSERT}", "DoCopy" Application.OnKey "^V", "DoPaste" Application.OnKey "^v", "DoPaste" Application.OnKey "+{INSERT}", "DoPaste" Application.OnKey "{ENTER}", "DoPaste" Application.OnKey "~", "DoPaste" 'Switch off drag/drop Application.CellDragAndDrop = False End Sub 'Handle Cutting cells Public Sub DoCut() If TypeOf Selection Is Range Then mbCut = True Set mrngSource = Selection Selection.Copy
Else Set mrngSource = Nothing Selection.Cut End If End Sub 'Handle Copying cells Public Sub DoCopy() If TypeOf Selection Is Range Then mbCut = False Set mrngSource = Selection Else Set mrngSource = Nothing End If Selection.Copy End Sub 'Handle pasting cells Public Sub DoPaste() If Application.CutCopyMode And Not mrngSource Is Nothing Then Selection.PasteSpecial xlValues If mbCut Then mrngSource.ClearContents End If Application.CutCopyMode = False Else ActiveSheet.Paste End If End Sub
Custom Command Bars Most dict at or applicat ions will include a set of m enus and t oolbars t o provide access t o t he applicat ion's funct ionalit y. Dict at or applicat ions usually have quit e com plex m enu st ruct ures, m ixing bot h Excel's m enu it em s ( such as Print and Print Preview) and cust om it em s. The m aint enance of t hese m enu it em s can be great ly eased by using a t able- driven approach t o building t he com m and bars, as discussed in Chapt er 8 Advanced Com m and Bar Handling.
Processing and Analysis Many dict at or applicat ions use Excel for it s dat a processing, calculat ion and analysis feat ures, rat her t han it s rich UI . All t he processing should be perform ed using hidden sheet s, under program cont rol, wit h only t he result s being shown t o t he users. This enables us t o design our processing sheet s for m axim um calculat ion efficiency, wit hout having t o worry whet her t hey would be readable
by our users. This t opic is covered in det ail in Chapt er 14 Dat a Manipulat ion Techniques.
Presenting Results Excel worksheet s are ext rem ely good present at ion vehicles for det ailed report s and chart s; indeed, t he requirem ent t o use Excel for t he applicat ion's report ing m echanism is oft en t he m ain fact or for choosing t o creat e t he applicat ion ent irely in Excel. I n pract ice, report st yles and layout s are usually dict at ed by t he client ( t o conform t o a house st yle) , but we explain how t o get t he m ost out of Excel's chart ing engine in Chapt er 15 Advanced Chart ing Techniques.
Practical Example PETRAS Timesheet The PETRAS t im esheet add- in has not been updat ed for t his chapt er.
PETRAS Reporting I n Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins, we int roduced t he client side of t he PETRAS applicat ion, wit h which our users ent er t heir weekly t im ekeeping inform at ion and save t he result ing workbook t o a cent ral locat ion on t he net work. This chapt er int roduces t he cent ral consolidat ion, analysis and report ing applicat ion, writ t en as a sim ple, single- workbook dict at or applicat ion. I n t his version of t he applicat ion, we're assum ing t he consolidat ion will be done weekly, as soon as all t he source t im esheet s have been received. The dat a will be ext ract ed from each t im esheet workbook and copied t o a single t able in a result s workbook, from which we'll generat e a single pivot t able t o provide a rudim ent ary analysis capabilit y. The result s workbook can t hen be saved. We also allow previous result s workbooks t o be opened, so t he consolidat ion can be repeat ed ( for inst ance, if a t im esheet arrives lat e) . Lat er chapt ers add m any m ore feat ures t o t he applicat ion. The applicat ion can be found on t he CD in t he folder \ Applicat ion\ Ch06Dict at or Applicat ions and includes t he following files: Pe t r a sTe m pla t e .x ls The client dat a- ent ry t em plat e, unchanged from Chapt er 5 Pe t r a sAddin .x la The client dat a- ent ry support add- in, un changed from Chapt er 5 Pe t r a sRe por t in g.x la The m ain report ing applicat ion Pe t r a sCon solida t ion .x lt A t em plat e t o use for new result s workbooks, cont aining an area for im port ing t im esheet dat a t o and a preform at t ed pivot t able, which references t he consolidat ion area D e bu g.in i A dum m y file t hat t ells t he applicat ion t o run in debug m ode The m ain report ing workbook, Pet rasReport ing.xla, cont ains t he following it em s: M Globa ls This m odule cont ains t he declarat ions of our global const ant s and variables. M Ope n Close This m odule cont ains t he st art up and shut down code, including code sim ilar t o List ing 6- 1 t o check t he Excel version.
M W or k spa ce This m odule cont ains t he code t o st ore, configure and rest ore t he Excel environm ent , very sim ilar t o List ing 6- 2 , List ing 6- 3 , List ing 6- 5 and List ing 6- 6 . M Com m a n dba r s This m odule cont ains code t o creat e and dest roy our m enus, including code lik e List ing 6- 4 t o rest ore t hem . M En t r y Poin t s This m odule cont ains t he rout ines called by our m enus. M St a n da r dCode This m odule cont ains t he WorkbookAlive funct ion shown in List ing 6- 6 , a funct ion t o check whet her a given file has a specific cust om docum ent propert y ( see below) and will cont ain m ore com m on ut ilit y rout ines as t hey're added t hroughout t he book. M Sy st e m Code This m odule cont ains code specific t o t his applicat ion, including a rout ine t o enable/ disable som e of our m enu it em s and t he m ain rout ine t o perform t he dat a consolidat ion. w k sBa ck D r op This is t he worksheet used for t he backdrop graphic. The exam ple applicat ion uses t he code shown in List ing 6- 8 t o set up t he m enu st ruct ure it em by it em . I t is quit e a lengt hy rout ine, for only eight m enu it em s. Fort unat ely, it will be replaced in Chapt er 8 Advanced Com m and Bar Handling, t o use a t able- driven com m and bar builder, enabling us t o im plem ent a m uch m ore com prehensive m enu st ruct ure wit hout adding new code for every it em .
List in g 6 - 8 . Code t o Se t Up t h e M e n u St r u ct u r e Sub SetUpMenus() Dim cbCommandBar As CommandBar Dim oPopup As CommandBarPopup Dim oButton As CommandBarButton ' Hide all the toolbars On Error Resume Next For Each cbCommandBar In Application.CommandBars cbCommandBar.Visible = False cbCommandBar.Enabled = False Next Application.CommandBars(gsMENU_BAR).Delete On Error GoTo 0 'Create our menu bar Set cbCommandBar = Application.CommandBars.Add( _ gsMENU_BAR, , True, True) 'The File menu Set oPopup = cbCommandBar.Controls.Add(msoControlPopup) With oPopup .Caption = "&File"
'File > New Set oButton = .Controls.Add(msoControlButton) With oButton .Caption = "&New Consolidation..." .BeginGroup = True .FaceId = 18 .ShortcutText = "Ctrl+N" .OnAction = "MenuFileNew" Application.OnKey "^N", "MenuFileNew" Application.OnKey "^n", "MenuFileNew" End With 'File > Open Set oButton = .Controls.Add(msoControlButton) With oButton .Caption = "&Open..." .BeginGroup = False .FaceId = 23 .ShortcutText = "Ctrl+O" .OnAction = "MenuFileOpen" Application.OnKey "^O", "MenuFileOpen" Application.OnKey "^o", "MenuFileOpen" End With 'File > Close Set oButton = .Controls.Add(msoControlButton) With oButton .Caption = "&Close" .BeginGroup = False .FaceId = 106 .OnAction = "MenuFileClose" .Enabled = False End With 'File > Save 'Use the standard Save button Set oButton = .Controls.Add(msoControlButton, 3) With oButton .BeginGroup = True .Enabled = False End With 'File > Save As 'Use the standard Save As button Set oButton = .Controls.Add(msoControlButton, 748) With oButton .BeginGroup = False .Enabled = False End With 'File > Exit Set oButton = .Controls.Add(msoControlButton)
With oButton .Caption = "&Exit" .BeginGroup = True .OnAction = "MenuFileExit" End With End With 'The Processing menu Set oPopup = cbCommandBar.Controls.Add(msoControlPopup) With oPopup .Caption = "&Processing" 'Processing > Consolidate Set oButton = .Controls.Add(msoControlButton) With oButton .Caption = "&Consolidate Timesheets" .BeginGroup = True .OnAction = "MenuConsolidate" .Enabled = False End With End With 'The Help menu Set oPopup = cbCommandBar.Controls.Add(msoControlPopup) With oPopup .Caption = "&Help" 'Help > About Set oButton = .Controls.Add(msoControlButton) With oButton .Caption = "&About PETRAS Reporting" .BeginGroup = True .OnAction = "MenuHelpAbout" End With End With cbCommandBar.Visible = True 'Protect the command bars, to prevent customization Application.CommandBars("Toolbar List").Enabled = False End Sub
Identifying Workbooks Dict at or applicat ions oft en need t o ident ify whet her a part icular workbook was creat ed from a part icular t em plat e, or ot herwise " belongs" t o t he applicat ion. One way t o do t his wit hout needing t o open t he workbook is t o add a cust om docum ent propert y t o t he t em plat e file, as shown in Figure 6- 1 .
Figu r e 6 - 1 . Addin g a n I de n t ifyin g Cu st om D ocu m e n t Pr ope r t y t o a Te m pla t e
The dict at or applicat ion can t hen use Excel's FileSearch obj ect t o t est whet her t he file has t hat propert y, using t he funct ion shown in List ing 6- 9 . I n our exam ple applicat ion, we've added t he Pet rasTim esheet propert y t o t he dat a- ent ry t em plat e and t he Pet rasResult s propert y t o t he consolidat ion result s t em plat e.
List in g 6 - 9 . Usin g t h e File Se a r ch Obj e ct t o Ch e ck for a Cu st om D ocu m e n t Pr ope r t y 'Function to test whether a file has the given 'Yes/No property set to Yes
Function FileHasYesProperty(ByVal sFile As String, _ ByVal sProperty As String) As Boolean Dim lSeparator As Long 'Using the FileSearch object With Application.FileSearch 'Start a new search .NewSearch 'For all file types .FileType = msoFileTypeAllFiles 'For the given file in the given directory lSeparator = InStrRev(sFile, "\") .Filename = Mid$(sFile, lSeparator + 1) .LookIn = Left$(sFile, lSeparator) 'Having the given Yes/No property set to Yes .PropertyTests.Add sProperty, msoConditionIsYes, _ Connector:=msoConnectorAnd 'Look for the file .Execute 'If we found it, that file has that property! FileHasYesProperty = .FoundFiles.Count > 0 End With End Function
Using the PETRAS Reporting Application I n Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins, we explained how t o use t he PETRAS t im esheet add- in and t em plat e t o creat e weekly t im esheet workbooks and st ore t hem in a cent ral locat ion. The following st eps can be used t o consolidat e and analyze your t im esheet s using t he PETRAS report ing dict at or applicat ion:
1 . St art Excel and use File > Open t o open t he Pet rasReport ing.xla workbook. 2 . Select t he File > New Consolidat ion m enu, as shown in Figure 6- 2 . This will creat e a new, em pt y consolidat ion workbook and t hen display a st andard file open dialog.
Figu r e 6 - 2 . Th e PETRAS Re por t in g Applica t ion M e n u St r u ct u r e
3 . Select t he t im esheet workbooks t o include in t he consolidat ion by m ult iselect ing t he individual files ( or using Ct rl+ A t o select all t he files in a folder) and click OK t o begin t he consolidat ion. The applicat ion will ext ract t he t im esheet dat a from all t he select ed files, im port ing it int o t he consolidat ion workbook. 4 . Review t he consolidat ed dat a in t he SourceDat a worksheet and analyze t he dat a in t he Pivot Table worksheet .
Conclusion Dict at or applicat ions enable us t o leverage Excel's rich user int erface, calculat ion engine, analysis and/ or present at ion feat ures, while sim ulat ing t he look and behavior of a norm al Windows pr ogr am . The Excel workspace will usually require a significant am ount of configurat ion and cust om izat ion for t he dict at or applicat ion. Unfort unat ely, Excel will rem em ber m ost of t hese configurat ions and t ry t o reuse t hem for t he next ( user) session. To work around Excel's behavior, we m ust st ore t he init ial st at e of t he user's set t ings when our applicat ion st art s and rest ore t hem when we close down. When using a worksheet - based user int erface, ext rem e care m ust be t aken t o prevent accident al corrupt ion of t he sheet , oft en as a result of a cut , copy or past e operat ion. The pract ical exam ple for t his chapt er is a com plet e, but sim ple, dict at or applicat ion t em plat e for you t o use as a st art ing point for your own applicat ions. Lat er chapt ers in t his book add m any m ore feat ures t o t he applicat ion.
Chapter 7. Using Class Modules to Create Objects Class m odules are used t o creat e obj ect s. There are m any reasons for you as a developer t o creat e your own obj ect s, including t he following: To encapsulat e VBA and Windows API code t o m ake it t ransport able and easy t o use and reuse, as shown in Chapt er 9 Underst anding and Using Windows API Calls To t rap event s To raise event s To creat e your own obj ect s and obj ect m odels I n t his chapt er we assum e you are already fam iliar wit h writ ing VBA code t o m anipulat e t he obj ect s in Excel and are fam iliar wit h t he Excel obj ect m odel t hat defines t he relat ionships am ong t hose obj ect s. We also assum e you are fam iliar wit h obj ect propert ies, m et hods and event s. I f you have writ t en code in t he ThisWorkbook m odule, any of t he m odules behind worksheet s or chart s or t he m odule associat ed wit h a userform , you have already worked wit h class m odules. One of t he key feat ures of t hese m odules, like all class m odules, is t he abilit y t o t rap and respond t o event s. The goal of t his chapt er is t o show you how t o creat e your own obj ect s. We begin by explaining how t o creat e a single cust om obj ect and t hen show how you can creat e a collect ion cont aining m ult iple inst ances of t he obj ect . We cont inue wit h a dem onst rat ion of how t o t rap and raise event s wit hin your classes.
Creating Objects Suppose we want t o develop code t o analyze a single cell in a worksheet and cat egorize t he ent ry in t hat cell as one of t he following: Em pt y Cont aining a label Cont aining a const ant num eric value Cont aining a form ula We can readily accom plish t his by creat ing a new obj ect wit h t he appropriat e propert ies and m et hods. Our new obj ect will be a Cell obj ect . I t will have an Analyze m et hod t hat det erm ines t he cell t ype and set s t he CellType propert y t o a num eric value t hat can be used in our code. We will also have a Descript iveCellType propert y so we can display t he cell t ype as t ext . List ing 7- 1 shows t he CCell class m odule code. This class m odule is used t o creat e a cust om Cell obj ect represent ing t he specified cell, analyze t he cont ent s of t he cell and ret urn t he t ype of t he cell as a user- friendly t ext st ring.
List in g 7 - 1 . Th e CCe ll Cla ss M odu le Option Explicit Public Enum anlCellType anlCellTypeEmpty anlCellTypeLabel anlCellTypeConstant anlCellTypeFormula End Enum Private muCellType As anlCellType Private mrngCell As Excel.Range Property Set Cell(ByRef rngCell As Excel.Range) Set mrngCell = rngCell End Property Property Get Cell() As Excel.Range Set Cell = mrngCell End Property
Property Get CellType() As anlCellType CellType = muCellType End Property Property Get DescriptiveCellType() As String Select Case muCellType Case anlCellTypeEmpty DescriptiveCellType = "Empty" Case anlCellTypeFormula DescriptiveCellType = "Formula" Case anlCellTypeConstant DescriptiveCellType = "Constant" Case anlCellTypeLabel DescriptiveCellType = "Label" End Select End Property Public Sub Analyze() If IsEmpty(mrngCell) Then muCellType = anlCellTypeEmpty ElseIf mrngCell.HasFormula Then muCellType = anlCellTypeFormula ElseIf IsNumeric(mrngCell.Formula) Then muCellType = anlCellTypeConstant Else muCellType = anlCellTypeLabel End If End Sub
The CCell class m odule cont ains a public enum erat ion wit h four m em bers, each of which represent s a cell t ype. By default , t he enum erat ion m em bers will be assigned values from zero t o t hree. The enum erat ion m em ber nam es help m ake our code m ore readable and easier t o m aint ain. The enum erat ion m em ber values are t ranslat ed int o user- friendly t ext by t he Descript iveCellType pr oper t y . List ing 7- 2 shows t he AnalyzeAct iveCell procedure. This procedure is cont ained in t he st andard m odule MEnt ryPoint s.
List in g 7 - 2 . Th e An a lyze Act ive Ce ll Pr oce du r e Public Sub AnalyzeActiveCell() Dim clsCell As CCell ' Create new instance of Cell object Set clsCell = New CCell ' Determine cell type and display it
Set clsCell.Cell = Application.ActiveCell clsCell.Analyze MsgBox clsCell.DescriptiveCellType End Sub
I f you select a cell on a worksheet and run t he AnalyzeAct iveCell procedure, it creat es a new inst ance of t he CCell class t hat it st ores in t he clsCell obj ect variable. The procedure t hen assigns t he act ive cell t o t he Cell propert y of t his Cell obj ect , execut es it s Analyze m et hod and displays t he result of it s Descript iveCellType propert y. This code is cont ained in t he Analysis1.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book .
Class Module Structure A class m odule cont ains t he blueprint for an obj ect . You can use it t o creat e as m any inst ances of t he obj ect as you require. I t defines t he m et hods and propert ies of t he obj ect . Any public subrout ines or funct ions in t he class m odule becom e m et hods of t he obj ect . Any public variables or propert y procedures becom e propert ies of t he obj ect .
Property Procedures Rat her t han rely on public variables t o define propert ies, it is bet t er pract ice t o use propert y procedures. These give you m ore cont rol over how propert ies are assigned values and how t hey ret urn values. Propert y procedures enable you t o validat e t he dat a t hat is passed t o t he obj ect and t o perform relat ed act ions where appropriat e. They also enable you t o m ake propert ies read only or writ e only if you want . The CCell class uses t wo privat e m odule level variables t o st ore it s propert ies int ernally. m uCellType holds t he cell t ype in t he form of an anlCellType enum erat ion m em ber value. m rngCell holds a reference t o t he single- cell Range t hat an obj ect creat ed from t he CCell class will r epr esent . Propert y procedures cont rol t he int erface bet ween t hese variables and t he out side world. Propert y procedures com e in t hree form s: Pr ope r t y Le t Used t o assign a value t o a propert y Pr ope r t y Se t Used t o assign an obj ect t o a propert y Pr ope r t y Ge t Used t o ret urn t he value or t he obj ect reference in a propert y t o t he out side w or ld The propert y nam e present ed t o t he out side world is t he sam e as t he nam e of t he propert y procedure. The CCell class uses Propert y Set Cell t o enable you t o assign a Range reference t o t he Cell propert y of t he Cell obj ect . The propert y procedure st ores t he reference in t he m rngCell
variable. This procedure could have a validat ion check t o ensure t hat only single- cell ranges can be specified. There is a corresponding Propert y Get Cell procedure t hat allows t his propert y t o be read. The CCell class uses t wo Propert y Get procedures t o ret urn t he cell t ype as an enum erat ion m em ber value or as descript ive t ext . These propert ies are read- only because t hey have no corresponding Propert y Let procedures.
Methods The CCell class has one m et hod defined by t he Analyze subrout ine. I t det erm ines t he t ype of dat a in t he cell referred t o by t he m rngCell variable and assigns t he corresponding enum erat ion m em ber t o t he m uCellType variable. Because it is a subrout ine, t he Analyze m et hod doesn't ret urn a value t o t he out side world. I f a m et hod is creat ed using a funct ion, it can ret urn a value. The Analyze m et hod could be convert ed t o a funct ion t hat ret urned t he t ext value associat ed wit h t he cell t ype, as shown in List ing 7- 3 .
List in g 7 - 3 . Th e An a lyze M e t h od of t h e Ce ll Obj e ct Public Function Analyze() As String If IsEmpty(mrngCell) Then muCellType = anlCellTypeEmpty ElseIf mrngCell.HasFormula Then muCellType = anlCellTypeFormula ElseIf IsNumeric(mrngCell.Formula) Then muCellType = anlCellTypeConstant Else muCellType = anlCellTypeLabel End If Analyze = Me.DescriptiveCellType End Function
We could t hen analyze t he cell and display t he ret urn value wit h t he following single line of code inst ead of t he original t wo lines:
MsgBox clsCell.Analyze()
Creating a Collection Now t hat we have a Cell obj ect , we will want t o creat e m any inst ances of t he obj ect so we can analyze a worksheet or ranges of cells wit hin a worksheet . The easiest way t o m anage t hese new obj ect s is t o st ore t hem in a collect ion. VBA provides a Collect ion obj ect t hat we can use t o st ore obj ect s and dat a. The Collect ion obj ect has four m et hods: Ad d Count I t em Rem ove There is no rest rict ion on t he t ype of dat a t hat can be st ored wit hin a Collect ion obj ect , and it em s wit h different dat a t ypes can be st ored in t he sam e Collect ion obj ect . I n our case, we want t o be consist ent and st ore j ust Cell obj ect s in our collect ion. To creat e a new Collect ion, t he first st ep is t o add a new st andard m odule t o cont ain global variables. This m odule will be called MGlobals. Next , add t he following variable declarat ion t o t he MGlobals m odule t o declare a global Collect ion obj ect variable t o hold t he collect ion, as follows:
Public gcolCells As Collection
Now add t he Creat eCellsCollect ion procedure shown in List ing 7- 4 t o t he MEnt ryPoint s m odule. The m odified code is cont ained in t he Analysis2.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book.
List in g 7 - 4 . Cr e a t in g a Colle ct ion of Ce ll Obj e ct s Public Sub CreateCellsCollection() Dim clsCell As CCell Dim rngCell As Range ' Create new Cells collection Set gcolCells = New Collection ' Create Cell objects for each cell in Selection For Each rngCell In Application.Selection
Set clsCell = New CCell Set clsCell.Cell = rngCell clsCell.Analyze 'Add the Cell to the collection gcolCells.Add Item:=clsCell, Key:=rngCell.Address Next rngCell ' Display the number of Cell objects stored MsgBox "Number of cells stored: " & CStr(gcolCells.Count) End Sub
We declare gcolCells as a public obj ect variable so t hat it persist s while t he workbook is open and is visible t o all procedures in t he VBA proj ect . The Creat eCellsCollect ion procedure creat es a new inst ance of t he collect ion and loops t hrough t he current ly select ed cells, creat ing a new inst ance of t he Cell obj ect for each cell and adding it t o t he collect ion. The address of each cell, in $A$1 reference st yle, is used as a key t o uniquely ident ify it and t o provide a way of accessing t he Cell obj ect lat er. We can loop t hrough t he obj ect s in t he collect ion using a For... Each loop or we can access individual Cell obj ect s by t heir posit ion in t he collect ion or by using t he key value. Because t he I t em m et hod is t he default m et hod for t he collect ion, we can use code like t he following t o access a specific Cell obj ect :
Set rngCell = gcolCells(3) Set rngCell = gcolCells("$A$3")
Creating a Collection Object The collect ion we have est ablished is easy t o use but it lacks som e feat ures we would like t o have. As it st ands, t here is no cont rol over t he t ype of obj ect s t hat can be added t o t he collect ion. We would also like t o add a m et hod t o t he collect ion t hat enables us t o highlight cells of t he sam e t ype and anot her m et hod t o rem ove t he highlight s. We will first add t wo new m et hods t o t he CCell class m odule. The Highlight m et hod adds color t o t he Cell obj ect according t o t he CellType. The UnHighlight m et hod rem oves t he color. List ing 7- 5 shows t he new code. Not e t hat we are applying t he principle of encapsulat ion. All t he code t hat relat es t o t he Cell obj ect is cont ained in t he CCell class m odule, not in any ot her m odule. Doing t his ensures t hat t he code can be easily found and m aint ained and m eans t hat it can be easily t ransport ed from one proj ect t o anot her .
List in g 7 - 5 . N e w Code for t h e CCe ll Cla ss M odu le
Public Sub Highlight() Cell.Interior.ColorIndex = Choose(muCellType + 1, 5, 6, 7, 8) End Sub Public Sub UnHighlight() Cell.Interior.ColorIndex = xlNone End Sub
We can now creat e a new class m odule nam ed CCells t o cont ain t he Cells collect ion, as shown in List ing 7- 6 . The com plet e code is cont ained in t he Analysis3.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book.
List in g 7 - 6 . Th e CCe lls Cla ssM odu le Option Explicit Private mcolCells As Collection Property Get Count() As Long Count = mcolCells.Count End Property Property Get Item(ByVal vID As Variant) As CCell Set Item = mcolCells(vID) End Property Private Sub Class_Initialize() Set mcolCells = New Collection End Sub Public Sub Add(ByRef rngCell As Range) Dim clsCell As CCell Set clsCell = New CCell Set clsCell.Cell = rngCell clsCell.Analyze mcolCells.Add Item:=clsCell, Key:=rngCell.Address End Sub Public Function NewEnum() As IUnknown Set NewEnum = mcolCells.[_NewEnum] End Function Public Sub Highlight(ByVal uCellType As anlCellType) Dim clsCell As CCell For Each clsCell In mcolCells If clsCell.CellType = uCellType Then clsCell.Highlight End If
Next clsCell End Sub Public Sub UnHighlight(ByVal uCellType As anlCellType) Dim clsCell As CCell For Each clsCell In mcolCells If clsCell.CellType = uCellType Then clsCell.UnHighlight End If Next clsCell End Sub
The m colCells Collect ion obj ect variable is declared as a privat e, m odule- level variable and is inst ant iat ed in t he I nit ialize procedure of t he class m odule. Because t he Collect ion obj ect is now hidden from t he out side world, we need t o writ e our own Add m et hod for it . We also have creat ed I t em and Count propert y procedures t o em ulat e t he corresponding propert ies of t he collect ion. The input argum ent for t he I t em propert y is declared as a Variant dat a t ype because it can be eit her a num eric index or t he st ring key t hat ident ifies t he collect ion m em ber. The Highlight m et hod loops t hrough each m em ber of t he collect ion. I f t he CellType propert y of t he Cell obj ect is t he sam e as t he t ype specified by t he uCellType argum ent , we execut e t he Cell obj ect 's Highlight m et hod. The UnHighlight m et hod loops t hrough t he collect ion and execut es t he UnHighlight m et hod of all Cell obj ect s whose t ype is t he sam e as t he t ype specified by t he uCellType argum ent . We've m odified t he public Collect ion variable declarat ion in MGlobals t o refer t o our new cust om collect ion class, as shown here:
Public gclsCells As CCells
We've also m odified t he Creat eCellsCollect ion procedure in t he MEnt ryPoint s m odule t o inst ant iat e and populat e our cust om collect ion, as shown in List ing 7- 7 .
List in g 7 - 7 . M En t r yPoin t s Code t o Cr e a t e a Ce lls Obj e ct Colle ct ion Public Sub CreateCellsCollection() Dim Dim Dim Dim
clsCell As CCell lIndex As Long lCount As Long rngCell As Range
Set gclsCells = New CCells For Each rngCell In Application.ActiveSheet.UsedRange
gclsCells.Add rngCell Next rngCell ' Count the number of formula cells in the collection. For lIndex = 1 To gclsCells.Count If gclsCells.Item(lIndex).CellType = anlCellTypeFormula Then lCount = lCount + 1 End If Next lIndex MsgBox "Number of Formulas = " & CStr(lCount) End Sub
We declare gclsCells as a public obj ect variable t o cont ain our cust om Cells collect ion obj ect . The Creat eCellsCollect ion procedure inst ant iat es gclsCells and uses a For...Each loop t o add all t he cells in t he act ive worksheet 's used range t o t he collect ion. Aft er loading t he collect ion, t he procedure count s t he num ber of cells t hat cont ain form ulas and displays t he result . The MEnt ryPoint s m odule cont ains a ShowForm ulas procedure t hat can be execut ed t o highlight and unhighlight t he form ula cells in t he worksheet . Several addit ional variat ions are provided for ot her cell t ypes. This code illust rat es t wo short com ings of our cust om collect ion class. You can't process t he m em bers of t he collect ion in a For...Each loop. You m ust use an index and t he I t em propert y inst ead. Also, our collect ion has no default propert y, so you can't short cut t he I t em propert y using t he st andard collect ion synt ax gclsCells( 1) t o access a m em ber of t he collect ion. You m ust specify t he I t em propert y explicit ly in your code. We explain how t o solve t hese problem s using Visual Basic or j ust a t ext edit or in t he next sect ion.
Addressing Class Collection Shortcomings I t is possible t o m ake your cust om collect ion class behave like a built - in collect ion. I t requires not hing m ore t han a t ext edit or t o m ake t he adj ust m ent s, but first we explain how t o do it by set t ing procedure at t ribut es using Visual Basic 6 ( VB6) t o bet t er illust rat e t he nat ure of t he changes r equir ed.
Using Visual Basic I n VB6, unlike Visual Basic for Applicat ions used in Excel, you can specify a propert y t o be t he default propert y of t he class. I f you declare t he I t em propert y t o be t he default propert y, you can om it .I t em when referencing a m em ber of t he collect ion and use a short cut such as gclsCells( 1) inst ead. I f you have VB6 inst alled you can export t he code m odule CCells t o a file and open t hat file in VB6. Place your cursor anywhere wit hin t he I t em propert y procedure and select Tools > Procedure At t r ibut es from t he m enu t o display t he Procedure At t ribut es dialog. Then click t he Advanced > >
but t on and under t he Advanced opt ions select ( Default ) from t he Procedure I D com bo box. This will m ake t he I t em propert y t he default propert y for t he class. When you save your changes and im port t his file back int o your Excel VBA proj ect , t he at t ribut e will be recognized even t hough t here is no way t o set at t ribut e opt ions wit hin t he Excel Visual Basic edit or. VB6 also enables you t o set up t he special procedure in List ing 7- 8 .
List in g 7 - 8 . Code t o Allow t h e Colle ct ion t o be Re fe r e n ce d in a For ... Each Loop Public Function NewEnum() As IUnknown Set NewEnum = mcolCells.[_NewEnum] End Function
This procedure m ust be given an at t ribut e value of 4, which you ent er direct ly int o t he Procedure I D com bo box in t he Procedure At t ribut es dialog. Giving t he NewEnum procedure t his at t ribut e value enables a For... Each loop t o process t he m em bers of t he collect ion. Aft er you have m ade t his addit ion t o your class m odule in VB6 and saved your changes, you can load t he m odule back int o your Excel VBA proj ect , and once again t he changes will be recognized.
Using a Text Editor Even wit hout VB6, you can easily creat e t hese procedures and t heir at t ribut es using a t ext edit or such as Not epad. Export t he CCells class m odule t o a file and open it using t he t ext edit or. Modify your code t o look like t he exam ple shown in List ing 7- 9 .
List in g 7 - 9 . Vie w in g t h e Code in a Te x t Edit or Property Get Item(ByVal vID As Variant) As CCell Attribute Item.VB_UserMemId = 0 Set Item = mcolCells(vID) End Property Public Function NewEnum() As IUnknown Attribute NewEnum.VB_UserMemId = -4 Set NewEnum = mcolCells.[_NewEnum] End Function
When t he m odified class m odule is im port ed back int o your proj ect , t he At t ribut e lines will not be visible, but t he procedures will work as expect ed. You can now refer t o a m em ber of t he collect ion as gclsCells( 1) and use your cust om collect ion class in a For ... Each loop as shown in List ing 7-
10 .
List in g 7 - 1 0 . Re fe r e n cin g t h e Ce lls Colle ct ion in a For ... Each Loop For Each clsCell In gclsCells If clsCell.CellType = anlCellTypeFormula Then lCount = lCount + 1 End If Next clsCell
Trapping Events A powerful capabilit y built in t o class m odules is t he abilit y t o respond t o event s. We want t o ext end our Analysis applicat ion so t hat when you double- click a cell t hat has been analyzed, all t he cells of t he sam e t ype will be highlight ed. When you right - click t he cell, t he highlight will be rem oved. We also want t o ensure t hat cells are reanalyzed when t hey are changed so t hat our corresponding Cell obj ect s are kept up t o dat e. The code shown in t his sect ion is cont ained in t he Analysis4.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book. To t rap t he event s associat ed wit h an obj ect you need t o do t wo t hings: Declare a W it h Ev e n t s variable of t he correct obj ect t ype in a class m odule. Assign an obj ect reference t o t he variable. The event s we want t o t rap are associat ed wit h t he Worksheet obj ect . Therefore, we need t o creat e a Wit hEvent s obj ect variable in t he CCells class m odule t hat references t he worksheet cont aining t he Cell obj ect s. The appropriat e Wit hEvent s variable declarat ion is m ade at t he m odule- level wit hin t he CCells class and looks like t he following:
Private WithEvents mwksWorkSheet As Excel.Worksheet
As soon as you add t his variable declarat ion t o t he CCells class m odule, you can select t he Wit hEvent s variable nam e from t he drop- down m enu at t he t op left of t he m odule and use t he drop- down m enu at t he t op right of t he m odule t o see t he event s t hat can be t rapped, as shown in Figure 7- 1 . Event nam es list ed in bold are current ly being t rapped wit hin t he class, as you will see in a m om ent .
Figu r e 7 - 1 . Th e W or k sh e e t Eve n t Pr oce du r e s Ava ila ble in CCe lls [View full size image]
Select ing an event from t he drop- down creat es a shell for t he event procedure in t he m odule. You need t o add t he procedures shown in List ing 7- 11 t o t he CCells class m odule. They include a new propert y nam ed Worksheet t hat refers t o t he Worksheet obj ect cont aining t he Cell obj ect s held by t he collect ion, as well as t he code for t he BeforeDoubleClick, BeforeRight Click and Change event s.
List in g 7 - 1 1 . Addit ion s t o t h e CCe lls Cla ss M odu le Property Set Worksheet(wks As Excel.Worksheet) Set mwksWorkSheet = wks End Property
Private Sub mwksWorkSheet_BeforeDoubleClick( _ ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then Highlight mcolCells(Target.Address).CellType Cancel = True End If End Sub Private Sub mwksWorkSheet_BeforeRightClick( _ ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then UnHighlight mcolCells(Target.Address).CellType Cancel = True End If End Sub Private Sub mwksWorkSheet_Change(ByVal Target As Range) Dim rngCell As Range
If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then For Each rngCell In Target.Cells mcolCells(rngCell.Address).Analyze Next rngCell End If End Sub
The Creat eCellsCollect ion procedure in t he MEnt ryPoint s m odule needs t o be changed as shown in List ing 7- 12. The new code assigns a reference t o t he act ive worksheet t o t he Worksheet propert y of t he Cells obj ect so t he worksheet 's event s can be t rapped.
List in g 7 - 1 2 . Th e Upda t e d Cr e a t e Ce llsColle ct ion Pr oce du r e in t h e M En t r yPoin t s M odu le Public Sub CreateCellsCollection() Dim clsCell As CCell Dim rngCell As Range Set gclsCells = New CCells Set gclsCells.Worksheet = ActiveSheet For Each rngCell In ActiveSheet.UsedRange gclsCells.Add rngCell Next rngCell End Sub
You can now execut e t he Creat eCellsCollect ion procedure in MEnt ryPoint s t o creat e a new collect ion wit h all t he links in place t o t rap t he double- click and right - click event s for t he cells in t he worksheet . Double- clicking a cell changes t he cell's background of all sim ilar cells t o a color t hat depends on t he cell's t ype. Right - clicking a cell rem oves t he background color.
Raising Events Anot her powerful capabilit y of class m odules is t he abilit y t o raise event s. You can define your own event s and t rigger t hem in your code. Ot her class m odules can t rap t hose event s and respond t o t hem . To illust rat e t his, we will change t he way our Cells collect ion t ells t he Cell obj ect s it cont ains t o execut e t he Highlight and UnHighlight m et hods. The Cells obj ect will raise an event t hat will be t rapped by t he Cell obj ect s. The code shown in t his sect ion is cont ained in t he Analysis5.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book. To raise an event in a class m odule you need t wo t hings: An Ev e n t declarat ion at t he t op of t he class m odule. A line of code t hat uses Ra ise Eve n t t o cause t he event t o t ake place. The code changes shown in List ing 7- 13 should be m ade in t he CCells class m odule.
List in g 7 - 1 3 . Ch a n ge s t o t h e CCe lls Cla ss M odu le t o Ra ise a n Eve n t Option Explicit Public Enum anlCellType anlCellTypeEmpty anlCellTypeLabel anlCellTypeConstant anlCellTypeFormula End Enum Private mcolCells As Collection Private WithEvents mwksWorkSheet As Excel.Worksheet Event ChangeColor(uCellType As anlCellType, bColorOn As Boolean) Public Sub Add(ByRef rngCell As Range) Dim clsCell As CCell Set clsCell = New CCell Set clsCell.Cell = rngCell Set clsCell.Parent = Me clsCell.Analyze mcolCells.Add Item:=clsCell, Key:=rngCell.Address End Sub Private Sub mwksWorkSheet_BeforeDoubleClick( _
ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then RaiseEvent ChangeColor( _ mcolCells(Target.Address).CellType, True) Cancel = True End If End Sub Private Sub mwksWorkSheet_BeforeRightClick( _ ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then RaiseEvent ChangeColor( _ mcolCells(Target.Address).CellType, False) Cancel = True End If End Sub
Not e t hat we m oved t he anlCellType Enum declarat ion int o t he parent collect ion class m odule. Now t hat we have creat ed an explicit parent - child relat ionship bet ween t he CCell and CCells class, any public t ypes used by bot h classes m ust reside in t he parent class m odule; ot herwise, circular dependencies bet ween t he classes t hat cannot be handled by VBA will be creat ed. I n t he declarat ions sect ion of CCells we declare an event nam ed ChangeColor t hat has t wo argum ent s. The first argum ent defines t he cell t ype t o be changed and t he second argum ent is a Boolean value t o indicat e whet her we are t urning color on or off. The BeforeDoubleClick and BeforeRight Click event procedures have been changed t o raise t he new event and pass t he cell t ype of t he t arget cell and t he on or off value. The Add m et hod has been updat ed t o set a new Parent propert y of t he Cell obj ect . This propert y holds a reference t o t he Cells obj ect . The nam e reflect s t he relat ionship bet ween t he Cells obj ect as t he parent obj ect and t he Cell obj ect as t he child obj ect . Trapping t he event raised by t he Cells obj ect in anot her class m odule is carried out in exact ly t he sam e way we have t rapped ot her event s. We creat e a Wit hEvent s obj ect variable and set it t o reference an inst ance of t he class t hat defines and raises t he event . The changes shown in List ing 7- 14 should be m ade t o t he CCell class m odule.
List in g 7 - 1 4 . Ch a n ge s t o t h e CCe ll Cla ss M odu le t o Tr a p t h e Ch a n ge Color Eve n t Option Explicit Private muCellType As anlCellType Private mrngCell As Excel.Range Private WithEvents mclsParent As CCells Property Set Parent(ByRef clsCells As CCells)
Set mclsParent = clsCells End Property Private Sub mclsParent_ChangeColor(uCellType As anlCellType, _ bColorOn As Boolean) If Me.CellType uCellType Then Exit Sub If bColorOn Then Highlight Else UnHighlight End If End Sub
A new m odule- level obj ect variable m clsParent is declared Wit hEvent s as an inst ance of t he CCells class. A reference t o a Cells obj ect is assigned t o m clsParent in t he Parent Propert y Set procedure. When t he Cells obj ect raises t he ChangeColor event , it will be t rapped by all t he Cell obj ect s. The Cell obj ect s will t ake act ion in response t o t he event if t hey are of t he correct cell t ype.
A Family Relationship Problem Unfort unat ely, we have int roduced a problem in our applicat ion. Running t he Creat eCellsCollect ion procedure m ult iple t im es creat es a m em ory leak. Norm ally when you overwrit e an obj ect in VBA, VBA cleans up t he old version of t he obj ect and reclaim s t he m em ory t hat was used t o hold it . You can also set an obj ect equal t o Not hing t o reclaim t he m em ory used by it . I t is good pract ice t o do t his explicit ly when you no longer need an obj ect , rat her t han relying on VBA t o do it .
Set gclsCells = Nothing
When you creat e t wo obj ect s t hat st ore references t o each ot her, t he syst em will no longer reclaim t he m em ory t hey used when t hey are set t o new versions or when t hey are set t o Not hing. When analyzing t he worksheet in Analysis5.xls wit h 574 cells in t he used range, t here is a loss of about 250KB of RAM each t im e Creat eCellsCollect ion is execut ed during an Excel session.
N OTE: I f you are running Windows NT, 2000 or XP, you can check t he am ount of RAM current ly used by Excel by pressing Ct rl+ Shift + Esc t o display t he Processes window in Task Manager and exam ining t he Mem Usage colum n for t he row where t he I m age Nam e colum n is EXCEL.EXE.
One way t o avoid t his problem is t o m ake sure you rem ove t he cross- references from t he linked obj ect s before t he obj ect s are rem oved. You can do t his by adding a m et hod such as t he Term inat e m et hod shown in List ing 7- 15 t o t he problem classes, in our case t he CCell class.
List in g 7 - 1 5 . Th e Te r m in a t e M e t h od in t h e CCe ll Cla ss M odu le Public Sub Terminate() Set mclsParent = Nothing End Sub
The code in List ing 7- 16 is added t o t he CCells class m odule. I t calls t he Term inat e m et hod of each Cell class cont ained in t he collect ion t o dest roy t he cross- reference bet ween t he classes.
List in g 7 - 1 6 . Th e Te r m in a t e M e t h od in t h e CCe lls Cla ss M odu le Public Sub Terminate() Dim clsCell As CCell For Each clsCell In mcolCells clsCell.Terminate Set clsCell = Nothing Next clsCell Set mcolCells = Nothing End Sub
The code in List ing 7- 17 is added t o t he Creat eCellsCollect ion procedure in t he MEnt ryPoint s m odule.
List in g 7 - 1 7 . Th e Cr e a t e Ce llsColle ct ion Pr oce du r e in t h e M En t r yPoin t s M odu le Public Sub CreateCellsCollection() Dim clsCell As CCell Dim rngCell As Range ' Remove any existing instance of the Cells collection If Not gclsCells Is Nothing Then gclsCells.Terminate Set gclsCells = Nothing End If
Set gclsCells = New CCells Set gclsCells.Worksheet = ActiveSheet For Each rngCell In ActiveSheet.UsedRange gclsCells.Add rngCell Next rngCell End Sub
I f Creat eCellsCollect ion finds an exist ing inst ance of gclsCells, it execut es t he obj ect 's Term inat e m et hod before set t ing t he obj ect t o Not hing. The gclsCells Term inat e m et hod it erat es t hrough all t he obj ect s in t he collect ion and execut es t heir Term inat e m et hods. I n a m ore com plex obj ect m odel wit h m ore levels, you could have obj ect s in t he m iddle of t he st ruct ure t hat cont ain bot h child and parent references. The Term inat e m et hod in t hese obj ect s would need t o run t he Term inat e m et hod of each of it s children and t hen set it s own Parent propert y t o Not hing.
Creating a Trigger Class I nst ead of raising t he ChangeColor event in t he CCells class m odule, we can set up a new class m odule t o t rigger t his event . Creat ing a t rigger class gives us t he opport unit y t o int roduce a m ore efficient way t o highlight our Cell obj ect s. We can creat e four inst ances of t he t rigger class, one for each cell t ype, and assign t he appropriat e inst ance t o each Cell obj ect . That m eans each Cell obj ect is only sent a m essage t hat is m eant for it , rat her t han hearing all m essages sent t o all Cell obj ect s. The t rigger class also enables us t o elim inat e t he parent / child relat ionship bet ween our CCells and CCell classes, t hus rem oving t he requirem ent t o m anage cross- references. Not e t hat it will not always be possible or desirable t o do t his. The code shown in t his sect ion is cont ained in t he Analysis6.xls workbook in t he \ Concept s\ Ch07Using Class Modules t o Creat e Obj ect s folder on t he CD t hat accom panies t his book. List ing 7- 18 shows t he code in a new CTypeTrigger class m odule. The code declares t he ChangeColor event , which now only needs one argum ent t o specify whet her color is t urned on or off. The class has Highlight and UnHighlight m et hods t o raise t he event .
List in g 7 - 1 8 . Th e CType Tr igge r Cla ss M odu le Option Explicit Public Event ChangeColor(bColorOn As Boolean) Public Sub Highlight() RaiseEvent ChangeColor(True) End Sub
Public Sub UnHighlight() RaiseEvent ChangeColor(False) End Sub
List ing 7- 19 cont ains t he changes t o t he CCell class m odule t o t rap t he ChangeColor event raised in CTypeTrigger. Depending on t he value of bColorOn, t he event procedure runs t he Highlight or UnHighlight m et hods.
List in g 7 - 1 9 . Ch a n ge s t o t h e CCe ll Cla ss M odu le t o Tr a p t h e Ch a n ge Color Eve n t of CType Tr igge r Option Explicit Private muCellType As anlCellType Private mrngCell As Excel.Range Private WithEvents mclsTypeTrigger As CTypeTrigger Property Set TypeTrigger(clsTrigger As CTypeTrigger) Set mclsTypeTrigger = clsTrigger End Property Private Sub mclsTypeTrigger_ChangeColor(bColorOn As Boolean) If bColorOn Then Highlight Else UnHighlight End If End Sub
List ing 7- 20 cont ains t he changes t o t he CCells m odule. An array variable m aclsTriggers is declared t o hold t he inst ances of CTypeTrigger. The I nit ialize event redim ensions m aclsTriggers t o m at ch t he num ber of cell t ypes and t he For ... Next loop assigns inst ances of CTypeTrigger t o t he array elem ent s. The Add m et hod assigns t he correct elem ent of m aclsTriggers t o each Cell obj ect according t o it s cell t ype. The result is t hat each Cell obj ect only list ens for m essages t hat apply t o it s own cell t ype.
List in g 7 - 2 0 . Ch a n ge s t o t h e CCe lls Cla ss M odu le t o Assign Re fe r e n ce s t o CType Tr igge r t o Ce ll Obj e ct s Option Explicit Public Enum anlCellType anlCellTypeEmpty
anlCellTypeLabel anlCellTypeConstant anlCellTypeFormula End Enum Private mcolCells As Collection Private WithEvents mwksWorkSheet As Excel.Worksheet Private maclsTriggers() As CTypeTrigger Private Sub Class_Initialize() Dim uCellType As anlCellType Set mcolCells = New Collection ' Initialize the array of cell type triggers, ' one element for each of our cell types. ReDim maclsTriggers(anlCellTypeEmpty To anlCellTypeFormula) For uCellType = anlCellTypeEmpty To anlCellTypeFormula Set maclsTriggers(uCellType) = New CTypeTrigger Next uCellType End Sub Public Sub Add(ByRef rngCell As Range) Dim clsCell As CCell Set clsCell = New CCell Set clsCell.Cell = rngCell clsCell.Analyze Set clsCell.TypeTrigger = maclsTriggers(clsCell.CellType) mcolCells.Add Item:=clsCell, Key:=rngCell.Address End Sub Public Sub Highlight(ByVal uCellType As anlCellType) maclsTriggers(uCellType).Highlight End Sub Public Sub UnHighlight(ByVal uCellType As anlCellType) maclsTriggers(uCellType).UnHighlight End Sub Private Sub mwksWorkSheet_BeforeDoubleClick( _ ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then Highlight mcolCells(Target.Address).CellType Cancel = True End If End Sub Private Sub mwksWorkSheet_BeforeRightClick( _ ByVal Target As Range, Cancel As Boolean) If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then UnHighlight mcolCells(Target.Address).CellType Cancel = True End If
End Sub Private Sub mwksWorkSheet_Change(ByVal Target As Range) Dim rngCell As Range Dim clsCell As CCell If Not Application.Intersect(Target, _ mwksWorkSheet.UsedRange) Is Nothing Then For Each rngCell In Target.Cells Set clsCell = mcolCells(rngCell.Address) clsCell.Analyze Set clsCell.TypeTrigger = _ maclsTriggers(clsCell.CellType) Next rngCell End If End Sub
Practical Example PETRAS Timesheet I n our pract ical exam ple for t his chapt er, we add an applicat ion- level event handling class t o our PETRAS t im esheet applicat ion t hat will m ake t wo significant changes. First , it will enable us t o convert t he t im e- ent ry workbook int o an Excel t em plat e. This will sim plify creat ion of new t im eent ry workbooks for new purposes as well as allow m ult iple t im e ent ry workbooks t o be open at t he sam e t im e. Second, t he event handler will aut om at ically det ect whet her a t im e ent ry workbook is act ive and enable or disable our t oolbar but t ons accordingly. Table 7- 1 sum m arizes t he changes m ade t o t he PETRAS t im esheet applicat ion for t his chapt er.
Ta ble 7 - 1 . Ch a n ge s t o PETRAS Tim e sh e e t Applica t ion for Ch a pt e r 7 M odu le
Pr oce du r e
Ch a n g e
Pet rasTem plat e.xlt
Changed t he norm al workbook int o a t em plat e w or k book
CAppEv ent Handler
Added an applicat ion- level event handling class t o t he add- in
MEnt r yPoint s
New Tim eSheet
New procedure t o creat e t im esheet s from t he t em plat e workbook
MOpenClose
Aut o_Open
Rem oved t im esheet init ializat ion logic and delegat ed it t o t he event handling class
MSyst em Code
Moved all t im e- ent ry workbook m anagem ent code int o t he event handling class
The Template A t em plat e workbook react s different ly t han a norm al workbook when opened using t he Excel Workbooks.Open m et hod. A norm al workbook will sim ply be opened. When a t em plat e workbook is opened a new, unsaved copy of t he t em plat e workbook will be creat ed. To creat e a t em plat e workbook from a norm al workbook, choose File > Save As from t he Excel m enu and select t he Tem plat e ent ry from t he Save as t ype drop- down. As soon as you select t he Tem plat e opt ion, Excel will unhelpfully m odify t he direct ory where you are saving your workbook t o t he Office Tem plat es direct ory, so don't forget t o change t his t o t he locat ion where you are st oring your applicat ion files.
Aft er we begin using a t em plat e workbook, t he user has com plet e cont rol over t he workbook filenam e. We will det erm ine whet her a given workbook belongs t o us by checking for t he unique nam ed const ant set I sTim eSheet t hat we have added t o our t em plat e workbook for t his purpose. A t em plat e workbook com bined wit h an applicat ion- level event handler enables us t o support m ult iple inst ances of t he t im e ent ry workbook being open sim ult aneously. This m ight be needed, for exam ple, if t here is a requirem ent t o have a separat e t im e sheet for each client or proj ect . Moving t o a t em plat e user int erface workbook also requires t hat we give t he user a way t o creat e new t im e sheet workbooks, because it is no longer a sim ple m at t er of opening and reusing t he sam e fixed t im esheet workbook over and over. I n Figure 7- 2 , not e t he new t oolbar but t on labeled New Tim e Sheet . This but t on enables t he user t o creat e new inst ances of our t em plat e.
Figu r e 7 - 2 . Th e PETRAS Toolba r w it h t h e N e w Tim e Sh e e t Bu t t on [View full size image]
As shown in List ing 7- 21, t he code run by t his new but t on is very sim ple.
List in g 7 - 2 1 . Th e N e w Tim e Sh e e t Pr oce du r e Public Sub NewTimeSheet() Application.ScreenUpdating = False InitGlobals Application.Workbooks.Add gsAppDir & gsFILE_TIME_ENTRY Application.ScreenUpdating = True End Sub
We t urn off screen updat ing and call I nit Globals t o ensure t hat our global variables are properly init ialized. We t hen sim ply open t he t em plat e workbook and t urn screen updat ing back on. When you open a t em plat e workbook from VBA, it is t reat ed different ly t han a norm al workbook. Rat her t han opening Pet rasTem plat e.xlt , a new copy of Pet rasTem plat e.xlt , called Pet rasTem plat e1, is creat ed. Each t im e t he user clicks t he New Tim e Sheet but t on, he get s a com plet ely new, independent copy of Pet rasTem plat e.xlt . The act of opening t he t em plat e t riggers t he NewWorkbook event in our event handing class. This event perform s all t he necessary act ions required t o init ialize t he t em plat e. This event procedure is shown in t he next sect ion.
The Application-Level Event Handler Wit hin our applicat ion- level event handling class, we encapsulat e m any of t he t asks t hat were previously accom plished by procedures in st andard m odules. For exam ple, t he MakeWorksheet Set t ings procedure and t he bI sTim eEnt ryBookAct ive funct ion t hat we encount ered in Chapt er 5 Funct ion, General and Applicat ion- Specific Add- ins are now bot h privat e procedures of t he class. We will describe t he layout of t he class m odule in List ing 7- 22, t hen explain what t he pieces do, inst ead of showing all of t he code here. You can exam ine t he code yourself in t he Pet rasAddin.xla workbook of t he sam ple applicat ion for t his chapt er on t he CD, and are st rongly encouraged t o do so.
List in g 7 - 2 2 . Cla ss M odu le La you t of t h e CAppEve n t H a n dle r Cla ss M odu le - Le ve l Va r ia ble s
Private WithEvents mxlApp As Excel.Application
Cla ss Eve n t Pr oce du r e s
Class_Initialize Class_Terminate mxlApp_NewWorkbook mxlApp_WorkbookOpen mxlApp_WindowActivate mxlApp_WindowDeactivate
Cla ss M e t h od Pr oce du r e s
SetInitialStatus
Cla ss Pr iva t e Pr oce du r e s
EnableDisableToolbar MakeWorksheetSettings bIsTimeEntryBookActive bIsTimeEntryWorkbook
Because t he variable t hat holds a reference t o t he inst ance of t he CAppEvent Handler class t hat we use in our applicat ion is a public variable, we use t he I nit Globals procedure t o m anage it . The code required t o do t his is shown below. I n t he declarat ions sect ion of t he MGlobals m odule:
Public gclsEventHandler As CAppEventHandler
I n t he I nit Globals procedure:
' Instantiate the Application event handler If gclsEventHandler Is Nothing Then Set gclsEventHandler = New CAppEventHandler End If
The I nit Globals code checks t o see whet her t he public gclsEvent Handler variable is init ialized and init ializes it if it isn't . I nit Globals is called at t he beginning of every nont rivial ent ry- point procedure in our applicat ion, so if anyt hing causes our class variable t o lose st at e, it will be inst ant iat ed again as soon as t he next ent ry- point procedure is called. This is a good safet y m echanism . When t he public gclsEvent Handler variable is init ialized, it causes t he Class_I nit ialize event procedure t o execut e. I nside t his event procedure, we init ialize t he event handling m echanism by set t ing t he class m odule- level Wit hEvent s variable t o refer t o t he current inst ance of t he Excel Applicat ion, as follows:
Set mxlApp = Excel.Application
Sim ilarly, when our applicat ion is exit ing and we dest roy our gclsEvent Handler variable, it causes t he Class_Term inat e event procedure t o execut e. Wit hin t his event procedure we dest roy t he class reference t o t he Excel Applicat ion obj ect by set t ing t he m xlApp variable t o Not hing. All t he rest of t he class event procedures, which are t hose belonging t o t he m xlApp Wit hEvent s variable, serve t he sam e purpose. They " wat ch" t he Excel environm ent and enable or disable our t oolbar but t ons as appropriat e when condit ions change. Disabling t oolbar but t ons when t hey can't be used is a m uch bet t er user int erface t echnique t han displaying an error m essage when t he user clicks one in t he wrong circum st ances. You don't want t o punish t he user ( t hat is, display an error m essage in response t o an act ion) when he can't be expect ed t o know he has done som et hing wrong. Not e t hat we always leave t he New Tim e Sheet and Exit PETRAS t oolbar but t ons enabled. The user should always be able t o creat e a new t im esheet or exit t he applicat ion.
I n addit ion t o enabling and disabling t he t oolbar but t ons, t he m xlApp_NewWorkbook and m xlApp_WorkbookOpen event procedures det ect when a t im e ent ry workbook is being creat ed or opened for t he first t im e, respect ively. At t his point t hey run t he privat e MakeWorksheet Set t ings procedure t o init ialize t hat t im e ent ry workbook. All of t he m xlApp event procedures are shown in List ing 7- 23. As you can see, t he individual procedures are very sim ple, but t he cum ulat ive effect is very powerful.
List in g 7 - 2 3 . Th e m x lApp Eve n t Pr oce du r e s Private Sub mxlApp_NewWorkbook(ByVal Wb As Workbook) If bIsTimeEntryWorkbook(Wb) Then EnableDisableToolbar True MakeWorksheetSettings Wb Else EnableDisableToolbar False End If End Sub Private Sub mxlApp_WorkbookOpen(ByVal Wb As Excel.Workbook) If bIsTimeEntryWorkbook(Wb) Then EnableDisableToolbar True MakeWorksheetSettings Wb Else EnableDisableToolbar False End If End Sub Private Sub mxlApp_WindowActivate(ByVal Wb As Workbook, _ ByVal Wn As Window) ' When a window is activated, check to see if it belongs ' to one of our workbooks. Enable all our toolbar controls ' if it does. EnableDisableToolbar bIsTimeEntryBookActive() End Sub Private Sub mxlApp_WindowDeactivate(ByVal Wb As Workbook, _ ByVal Wn As Window) ' When a window is deactivated, disable our toolbar ' controls by default. They will be re-enables by the ' WindowActivate event procedure if required. EnableDisableToolbar False End Sub
The full power of having an event handling class in your applicat ion is difficult t o convey on paper. We urge you t o experim ent wit h t he sam ple applicat ion for t his chapt er t o see for yourself how it works in a live set t ing. Double- click t he Pet rasAddin.xla file t o open Excel and see how t he applicat ion t oolbar behaves. Creat e new t im esheet workbooks, open non- t im esheet workbooks and
swit ch back and fort h bet ween t hem . The st at e of t he t oolbar will follow your every act ion. I t is also educat ional t o see exact ly how m uch preparat ion t he applicat ion does when you creat e a new inst ance of t he t im esheet workbook. Wit hout t he Pet rasAddin.xla running, open t he Pet rasTem plat e.xlt workbook and com pare how it looks and behaves in it s raw st at e wit h t he way it looks and behaves as an inst ance of t he t im esheet wit hin t he running applicat ion.
PETRAS Reporting The PETRAS report ing applicat ion has been m odified in m uch t he sam e way, and for t he sam e reasons, as t he PETRAS t im esheet add- in. By adding a class m odule t o handle applicat ion- level event s, we can enable t he user t o have m ult iple consolidat ion workbooks open at t he sam e t im e and swit ch bet ween t hem using t he new Window m enu, as shown in Figure 7- 3 .
Figu r e 7 - 3 . Th e PETRAS Re por t in g M e n u Ba r w it h t h e N e w W in dow M e n u
Table 7- 2 sum m arizes t he changes m ade t o t he PETRAS report ing applicat ion for t his chapt er. Rat her t han repeat m uch of t he previous few pages, we suggest you review t he Pet rasReport ing.xla workbook t o see exact ly how t he m ult iple- docum ent int erface has been im plem ent ed.
Ta ble 7 - 2 . Ch a n ge s t o t h e PETRAS Re por t in g Applica t ion for Ch a pt e r 7 M odu le
Pr oce du r e
CAppEv ent Handler
MCom m andBar s
Ch a n g e Added an applicat ion- level event handling class t o t he applicat ion t o m anage m ult iple consolidat ion workbooks.
Set UpMenus
Added code t o creat e t he Window m enu.
M odu le
Pr oce du r e
MSyst em Code
MEnt r yPoint s
Ch a n g e Added procedures t o add, rem ove and place a checkm ark against an it em in t he Window m enu.
MenuWindow Select
New procedure t o handle select ing an it em wit hin t he Window m enu. All Window m enu it em s call t his rout ine.
Conclusion You use class m odules t o creat e obj ect s and t heir associat ed m et hods, propert ies and event s. You can collect child obj ect s in a parent obj ect so t hat you can creat e a hierarchy of obj ect s t o form an obj ect m odel. You can use class m odules t o t rap t he event s raised by ot her obj ect s including t he Excel applicat ion. You can raise your own event s in a class m odule. When you set up cross- references bet ween parent and child obj ect s so t hat each is aware of t he ot her, you will creat e a st ruct ure t hat is not sim ple t o rem ove from m em ory when it is no longer useful. You need t o add ext ra code t o rem ove t hese cross- references. Class m odules are a powerful addit ion t o a developer's t oolkit . The obj ect s creat ed lead t o code t hat is easier t o writ e, develop, m aint ain and share t han t radit ional code. Obj ect s are easy t o use because t hey encapsulat e com plex code in a form t hat is very accessible. All you need t o know t o use an obj ect are it s m et hods, propert ies and event s. Obj ect s can be shared because t he class m odules t hat define t hem are encapsulat ed ( self- cont ained) and t herefore very t ransport able from one proj ect t o anot her. All you need t o do is copy t he class m odule t o m ake t he obj ect available in anot her proj ect . As a developer, you can easily add new m et hods, propert ies and event s t o an obj ect wit hout changing t he exist ing int erface. Your obj ect s can evolve wit hout harm ing older syst em s t hat use t hem . Most developers find class m odules very addict ive. The m ore you use t hem , t he m ore you like t hem and t he m ore uses you find for t hem . They will be used ext ensively t hroughout t he rest of t his book.
Chapter 8. Advanced Command Bar Handling This chapt er st art s off by covering som e best pract ices for com m and bar design. Then we int roduce our t able- driven com m and bar building m et hodology. This feat ure, which you can easily add t o your own applicat ions, rem oves m ost of t he difficult ies associat ed wit h building and m aint aining nont rivial cust om com m and bars. We show you how t o creat e and use cust om icon/ m ask file com binat ions in Excel 2002 and higher ( a feat ure also support ed by t he com m and bar builder) . We t hen finish up by explaining how t o hook Com m andBarCont rol event s in order t o int ercept clicks from Com m andBarCont rols in ways t hat are not possible using a sim ple OnAct ion m acro assignm ent .
N OTE Whenever we use t he noun con t r ol in t his chapt er, we are referring t o t he generic Com m andBarCont rol obj ect . This noun is used whenever t he t opic we are current ly discussing applies equally t o any of t he specific cont rol obj ect s t hat can be represent ed by a generic Com m andBarCont rol.
Command Bar Design Before we get int o com m and bar creat ion, let 's cover a few best pract ices for com m and bar design. Try t o follow all of t hese pract ices when designing your cust om com m and bars and cont rols: Em ulat e Excel's m enu bar. Users are already fam iliar wit h t he Excel m enu st ruct ure, so if your applicat ion em ulat es it users will have som e im m ediat e fam iliarit y wit h your applicat ion. This is especially t rue for dict at or applicat ions, whose m enus m ay include a large num ber of t he sam e feat ures t hat would norm ally appear on Excel's m enu bar. Don't use m ore t han t hree m enu sublevels. Alt hough t he t able- driven com m and bar builder t hat we will describe in t he next sect ion provides a m eans t o creat e m enus deeper t han t hree levels, t his is not som et hing you should do unless absolut ely necessary. Users t end t o get lost when your m enu st ruct ure exceeds t hree sublevels. I f you are adding one or m ore t op- level m enus t o t he built - in m enu bar, t hose m enus should be locat ed direct ly t o t he left of t he Window m enu. This is a longst anding user int erface convent ion t hat m ost users are fam iliar wit h. Unless you have a very good reason for not doing so, always dock your cust om t oolbars at t he t op of t he Excel window. There are a reasonable num ber of sit uat ions where it is appropriat e t o creat e float ing t oolbars, but rem em ber t hat your users can always undock your t oolbar if t hey want . Even less frequent ly, but st ill on occasion, it is useful t o dock a t oolbar at t he bot t om of t he Excel window. Right and left docking, even t hough t he com m and bar builder support s t hem , are never recom m ended. We have never seen a sit uat ion where t hese posit ions are appropriat e. The vast m aj orit y of users have never encount ered a t oolbar docked on t he right or left sides, and t he necessit y of doing so is alm ost always an indicat ion of poor user int erface design. Physically group cont rols t hat perform relat ed funct ions. Your applicat ion will be m uch easier t o use if sim ilar cont rols are locat ed near each ot her. Separat e relat ed groups of cont rols from each ot her using separat or bars, but t ry not t o have m ore t han four our five m enus or t oolbar but t ons in a row wit hout a separat or. Separat or bars are creat ed using t he BeginGroup propert y, which we cover in t he next sect ion. Select or creat e icons whose appearance visually im plies t he act ion perform ed by t heir cont rol. This seem s obvious, but it 's very difficult in a cust om applicat ion wit h a large num ber of feat ures. Put as m uch t hought and creat ivit y int o t he appearance of your icons as possible. Feat ures whose icons bear no visual resem blance t o t heir funct ion will be m uch harder for users t o rem em ber t han t hose whose icons clearly im ply t heir funct ion. Think, for exam ple, how easy it is t o det erm ine which t oolbar but t on you need t o click t o print a docum ent . Do your best t o em ulat e t his close associat ion bet ween funct ion and appearance.
Table-Driven Command Bars For sm all- scale Excel applicat ions wit h a few t oolbar but t ons and/ or m enu it em s, it is perfect ly accept able t o hard- code t he creat ion of t he com m and bars and cont rols your applicat ion requires using a cust om VBA procedure. ( We do recom m end t hat you isolat e com m and bar building code in a separat e procedure for ease of m aint enance.) When you begin building large- scale applicat ions wit h m ult iple dedicat ed t oolbars and m enus, hard- coded com m and bars becom e very t im econsum ing t o creat e and difficult t o m aint ain. The solut ion t o t his problem is a widely used t echnique wit hin t he Excel developm ent com m unit y known as t a ble - dr ive n com m and bar building. As we discussed in Chapt er 4 Worksheet Design, t able- driven is a generic t erm referring t o any process t hat perform s som e operat ion guided by inform at ion st ored in a dedicat ed t able on a worksheet . Table- driven com m and bar building is one of t he m ore com plex t able- driven m et hodologies. I m plem ent ed correct ly, however, it is easy t o use and far superior t o anyt hing t hat can be accom plished using VBA alone. Even when we resort t o using Visual Basic Act iveX DLLs, a t echnique we cover ext ensively in Chapt er 20 Com bining Excel and Visual Basic 6, we use an Excel add- in workbook wit h a t hree- line st ub procedure assigned t o each of t he com m and bar cont rols t hat calls t he Act iveX DLL. This add- in workbook allows us t o use worksheet s, which would ot herwise be unavailable, t o im plem ent t able- driven com m and bars and ot her t able- driven m et hodologies. I n t his sect ion we describe t he m ost sophist icat ed t able- driven com m and bar builder available. As wit h all ot her applicat ions described in t he book, t his com m and bar builder is included on t he CD t hat accom panies t his book and can be int egrat ed int o any of your applicat ions.
Introducing the Table-Driven Command Bar Builder The t able- driven com m and bar builder consist s of t hree part s t hat form a self- cont ained unit you can plug direct ly int o any applicat ion: a worksheet t able t hat defines t he com m and bars and cont rols t o be built and t wo code m odules t hat read t his t able and build t he com m and bars and cont rols it specifies. To use t he com m and bar builder in your applicat ion, you need t o copy t he wksCom m andBars worksheet and t he MCom m andBars and MPast ePict ure m odules int o your proj ect . I n t he Put t ing I t All Toget her sect ion lat er in t he chapt er we dem onst rat e t he workbook cont aining t he version of t he com m and bar builder t hat you should use in your proj ect s. Aft er you have added definit ions of t he com m and bars and cont rols you want t o build t o t he wksCom m andBars worksheet , your applicat ion j ust needs t o call one procedure on st art up and all of t hose com m and bars and cont rols will be built t o your specificat ions. A second procedure can be called on shut down t o dism ant le t he com m and bars and cont rols specified in t he t able. We cover t his in m ore det ail in t he Pract ical Exam ple sect ion at t he end of t he chapt er. We spend m ost of t his sect ion explaining how t o writ e com m and bar definit ions in t he com m and
bar definit ion t able. I t 's best t o t hink of t he code t hat reads and im plem ent s t he com m and bars and cont rols defined by t he t able as a black box. This code is t oo lengt hy and com plex t o cover in any det ail in t his chapt er, but you are st rongly encouraged t o exam ine t he code if you want t o underst and how it works. The code is open and heavily com m ent ed, so it should be reasonably approachable t o t he seasoned program m er.
The Command Bar Definition Table The reason we are spending so m uch t im e describing t he com m and bar definit ion t able is because you will spend 99 percent of your com m and bar building t im e working wit h it . The only t hing you need t o do wit h t he code, aft er adding it t o your proj ect , is call t he build com m and bars procedure on st art up and t he reset com m and bars procedure on shut down. The com m and bar definit ion t able is t oo wide t o display ent irely in a screen shot on t he print ed page, but we will give you t he flavor for what it looks like wit h t he part ial exam ple shown in Figur e 8 - 1.
Figu r e 8 - 1 . A Pa r t ia l Com m a n d Ba r D e fin it ion Ta ble [View full size image]
The cust om m enu bar creat ed by t his com m and bar definit ion t able ent ry is shown in Figure 8- 2 .
Figu r e 8 - 2 . A Cu st om M e n u Ba r Cr e a t e d by t h e Com m a n d Ba r Bu ilde r
Keep in m ind t his is only a sm all sect ion from t he upper- left corner of t he com m and bar definit ion t able. The act ual t able cont ains at least 25 colum ns of set t ings and as m any rows as required t o define all of t he com m and bars and cont rols required for t he applicat ion. One of t he feat ures m aking t he com m and bar definit ion t able so easy for experienced program m ers t o use is t hat it cont ains sheet - level defined const ant s corresponding t o every enum erat ion m em ber used when building com m and bars and cont rols wit h VBA. Therefore, wherever t he t able calls for an enum erat ion m em ber such as msoBarTop or msoButtonIconAndCaption, you can use t hose nam es exact ly as you would in VBA by j ust preceding t hem wit h an equal sign when you ent er t hem int o a cell. Their num eric values will appear in t he cell, but if you select t he cell and check t he form ula bar you will see t he enum erat ion m em ber nam e. Figure 8- 3 shows an exam ple of t his.
Figu r e 8 - 3 . D e fin e d Con st a n t s in t h e Com m a n d Ba r D e fin it ion Ta ble
I n t he sect ions t hat follow, we will provide com plet e descript ions of t he purpose and usage of each of t he set t ings in t he com m and bar definit ion t able. I n t he act ual t able t here are cell com m ent s at t he t op of each colum n t hat give brief but reasonably com plet e explanat ions of t he purpose of t he colum n. This will enable you t o use t he t able wit hout having t o cont inually refer back t o t his chapt er. The set t ing nam es in t he colum n headers of t he com m and bar definit ion t able are ident ical or very sim ilar t o t he nam es of t he VBA propert ies t hey represent on t he com m and bar or cont rol being defined. This allows you t o leverage all of your exist ing knowledge of how t o build com m and bars and cont rols in VBA when using t he t able- driven com m and bar builder. Most set t ings in t he com m and bar definit ion t able are not required. I n addit ion, som e set t ings apply t o Com m andBar obj ect s, som e t o Com m andBarCont rol obj ect s and som e t o bot h. Table 8- 1 cont ains a sum m ary of t he com m and bar definit ion t able set t ings, showing which obj ect s each set t ing applies t o, which set t ings are required and what t he default value is for each opt ional set t ing, if any.
Ta ble 8 - 1 . Com m a n d Ba r D e fin it ion Ta ble Se t t in gs Su m m a r y
Se t t in g
Com m a n dBa r
Com m a n d Ba r Con t r ol
Re qu ir e d
D e fa u lt
Com m and Bar Nam e
Yes
None
Cont rol Capt ion
Yes
None
Posit ion
No
m soBar Top
I sMenubar
No
False
Visible
No
False
Widt h
No
None
Pr ot ect ion
No
m soBar NoCust om ize
I sTem por ar y
No
True
I sEnabled
No
True
OnAct ion
No
None
Cont rol I D
No
1 ( Cust om )
Cont rol Type
No
m soCont r olBut t on
Cont rol St yle
No
m soBut t onAut om at ic
Face I D
No
None
Begin Group
No
False
Se t t in g
Com m a n dBa r
Com m a n d Ba r Con t r ol
Re qu ir e d
D e fa u lt
Befor e
No
Syst em Default
Toolt ip
No
Syst em Default
Short cut Text
No
None
Tag
No
None
Par am et er
No
None
St at e
No
m soBut t onUp
List Range
No
None
List s
No
None
Beginning wit h t he Posit ion colum n and cont inuing t hrough t he last colum n in t he t able, t he colum n values apply t o t he com m and bar or cont rol whose nam e or capt ion is specified in t he sam e row in one of t he first four colum ns of t he t able. I f a set t ing does not apply t o t he t ype of obj ect specified in t he init ial colum ns, it will be ignored. Sim ilarly, if t he ent ry in t he init ial colum ns specifies an exist ing com m and bar or cont rol obj ect , m ost set t ings in subsequent colum ns are ignored.
Command Bar Name The com m and bar builder has t he flexibilit y t o creat e new com m and bars as well as add cont rols t o exist ing com m and bars. Regardless of whet her you are creat ing a new com m and bar or adding cont rols t o an exist ing com m and bar, you ent er t he nam e of t he com m and bar in t his colum n. The com m and bar builder checks each com m and bar nam e specified in t his colum n t o see whet her it already exist s. I f t he specified com m and bar already exist s, t he com m and bar builder assum es you are adding cont rols t o t hat com m and bar. I f no com m and bar wit h t he specified nam e exist s, t he com m and bar builder creat es a new com m and bar wit h t he specified nam e using t he set t ings specified in lat er colum ns. There m ust be t wo ent ries in t he Com m and Bar Nam e colum n for every com m and bar you build or m odify. The first ent ry m ust be t he nam e or index num ber of t he com m and bar being creat ed or m odified. The second ent ry is sim ply t he word St op. The St op ent ry m ust be placed in t he row direct ly below t he last row of t he com m and bar definit ion specified by t he first ent ry. These t wo ent ries in t he first colum n work t oget her t o bracket t he com m and bar definit ion so t he com m and bar building code knows where t he definit ion st art s and ends. I n Figure 8- 1 , not ice how t he com m and bar nam e Cust om Menu Bar is placed at t he t op of t he Com m and Bar Nam e colum n in cell A2 and t he word St op is placed at t he bot t om in cell A27. As shown in t his exam ple, t h e r e ca n n ot be a n y e n t r ie s be t w e e n t h e com m a n d ba r n a m e va lu e a n d t h e St op k e yw or d. You can st ack as m any com m and bar definit ions in t he t able as you like. The only rule is t hat each subsequent definit ion m ust be separat ed from t he previous definit ion by at least one blank row.
Control Caption There are t hree Cont rol Capt ion colum ns by default . This is because good user int erface design suggest s you should not use m ore t han t hree cascading m enu levels. I f you really m ust have addit ional levels, you can sim ply insert addit ional Cont rol Capt ion colum ns t o t he right of t he exist ing t hree. Sim ilar t o t he Com m and Bar Nam e colum n, t he Cont rol Capt ion colum ns can be used t o creat e new cont rols or add subcont rols t o exist ing cont rols. I f t he com m and bar builder code det ect s t hat t he capt ion in t he Cont rol Capt ion colum n refers t o an exist ing cont rol on t he current com m and bar, it will assum e you want t o add subcont rols t o it . Ot herwise it will creat e a new cont rol wit h t he specified capt ion using t he set t ings specified in lat er colum ns. Regardless of whet her you are creat ing a single cont rol or a cascading series of m enus, each cont rol m ust occupy it s own row. The posit ion of a cont rol's capt ion wit hin t he series of Cont rol Capt ion colum ns det erm ines t he level at which t he cont rol will be added. Look again at Figure 8- 1 . Not ice t hat even t hough all t hree Cont rol Capt ion colum ns have ent ries, no row has a Cont rol Capt ion ent ry in m ore t han one of t he t hree colum ns. Th is is a n a bsolu t e r e qu ir e m e n t. You can provide an accelerat or key for your cont rol by placing an am persand direct ly t o t he left of t he charact er t hat you want t o use as t he accelerat or key charact er. The cont rol can t hen be act ivat ed from t he keyboard by pressing t he Alt key and t he specified charact er sim ult aneously. This feat ure only applies t o cont rols t hat display t heir capt ion and are current ly visible.
N OTE When a cont rol displays a dialog, st andard user int erface convent ions dict at e t hat t he capt ion of t he cont rol, if it displays one, should be followed by an ellipsis. See t he Excel File > Print …m enu for an exam ple of t his convent ion.
Position The Posit ion set t ing applies only t o Com m andBar obj ect s. I t specifies t he posit ion on t he screen where t he Com m andBar will appear when it is displayed. This set t ing m ust be one of t he following msoBarPosition enum erat ion m em bers: msoBarBottom The com m and bar will be docked at t he bot t om of t he screen. msoBarFloating The com m and bar will not be docked but inst ead will float over t he screen. msoBarLeft The com m and bar will be docked on t he left side of t he screen. msoBarPopup This set t ing is used t o specify com m and bars t hat will be displayed when t he user right - clicks wit h t he m ouse. The com m and bar will be displayed at t he posit ion where t he user right - clicked. Com m and bars wit h t his posit ion set t ing m ust be displayed in response t o
one of t he BeforeRight Click event procedures using t he synt ax: Application.CommandBars("Name").ShowPopup
msoBarRight The com m and bar will be docked on t he right side of t he screen. msoBarTop The com m and bar will be docked at t he t op of t he screen. This is t he default value if no posit ion is specified.
IsMenubar The I sMenubar set t ing applies only t o Com m andBar obj ect s. I f set t o True, t he specified com m and bar will be t he m enu bar when it is visible. You can define m ult iple com m and bars as m enu bars for different purposes, but only one m enu bar can be visible at a t im e. I f t he I sMenubar set t ing is False, t he com m and bar will be a t oolbar or popup depending on t he Posit ion set t ing. The I sMenubar propert y m ust be False for com m and bars wit h t he Posit ion propert y value msoBarPopup or a runt im e error will occur. Therefore, t he com m and bar builder code will enforce t his value for msoBarPopup com m and bars regardless of t he value act ually ent ered in t he t able. The default value for t he I sMenubar set t ing is False.
Visible The Visible set t ing applies only t o Com m andBar obj ect s. I f set t o True, t he specified Com m andBar will be visible, subj ect t o t he following lim it at ions: I f m ore t han one com m and bar has bot h t he I sMenubar and Visible set t ings set t o True, t he last such com m and bar in t he t able will be t he m enu bar t hat is act ually displayed. All ot her m enu bars will be hidden. The Visible propert y does not apply t o and has no effect on com m and bars wit h t he Posit ion value msoBarPopup. The default value of t he Visible set t ing is False. This enables you t o creat e a large num ber of com m and bars when your applicat ion st art s up and t hen display t hem on dem and as needed.
Width The Widt h set t ing applies t o Com m andBar and Com m andBarCont rol obj ect s. The Widt h set t ing m ust be a posit ive whole num ber great er t han zero. This set t ing is not required and t here is no default value. I f t he Widt h set t ing is not specified, t he widt h of t he com m and bar or cont rol will be det erm ined aut om at ically by VBA. I f t he Widt h set t ing is not specified for a com m and bar, VBA will m ake t he com m and bar wide enough t o display all of t he cont rols it cont ains on a single row. I f t he Widt h set t ing is not specified for a cont rol, VBA will m ake t he cont rol wide enough t o display it s icon and/ or capt ion.
For Com m andBar obj ect s, t he Widt h set t ing applies only when t he Posit ion set t ing is msoBarFloating. The Widt h set t ing is ignored for all ot her com m and bar Posit ion set t ings. You cannot m ake a com m and bar wider t han it s aut om at ically calculat ed widt h. Set t ing t he widt h of a float ing com m and bar t o a value narrower t han it s aut om at ically calculat ed widt h enables you t o st ack cont rols in m ult iple rows rat her t han displaying a long, single- row com m and bar. For Com m andBarCont rol obj ect s, t he Widt h set t ing always applies and you can set it t o any posit ive whole num ber great er t han zero. I f t he specified widt h is t oo narrow t o display t he capt ion and/ or icon of t he cont rol, however, it will be ignored. Not e t hat all cont rols on t he sam e popup m enu list will have t he widt h of t he widest cont rol in t he list regardless of t heir individual Widt h set t ings. There are no hard- and- fast rules for deciding exact ly what t he Widt h set t ing should be. The best approach is t o first build your com m and bars and cont rols wit hout specifying t he Widt h set t ing. Then use t he I m m ediat e window t o exam ine t he widt h propert y t hat has been aut om at ically assigned by VBA. You can use t hat as a st art ing point from which t o increase or decrease t he widt h of your com m and bars and/ or cont rols.
Protection The Prot ect ion set t ing applies only t o Com m andBar obj ect s. This set t ing specifies what t ype of m odificat ions t he user will be allowed t o m ake t o t he com m and bar. This set t ing m ust be one or m ore of t he following msoBarProtection enum erat ion m em bers. To apply m ult iple Prot ect ion values, j ust add t he values t oget her in t he Prot ect ion cell for t he com m and bar in quest ion. msoBarNoChangeDock The user cannot change t he posit ion at which t he com m and bar is dock ed. msoBarNoChangeVisible The user cannot change t he visibilit y st at us of t he com m and bar. I f t he com m and bar is visible, t he user cannot hide it ; if t he com m and bar is hidden, t he user cannot display it . msoBarNoCustomize The user cannot add or rem ove cont rols on t he com m and bar. msoBarNoHorizontalDock The user cannot dock t he com m and bar in any horizont al posit ion, eit her t op or bot t om . Wit hout any addit ional prot ect ion values, t he com m and bar can st ill be docked vert ically. To prevent a com m and bar from being docked anywhere, j ust add t he msoBarNoHorizontalDock and t he msoBarNoVerticalDock enum erat ion m em ber values t oget her in t he Prot ect ion cell. msoBarNoMove The com m and bar cannot be m oved. Be careful wit h t his opt ion because it will prevent t he user from m oving t he com m and bar under any circum st ances. For exam ple, if you creat e a float ing com m and bar whose widt h causes it t o appear part ially off- screen, t he user will not be able t o m ove t he com m and bar int o a posit ion where t hey can access all of it s cont rols. msoBarNoProtection The user can m ake any changes t o t he com m and bar t hat he want s. msoBarNoResize The user cannot m odify t he widt h or height of t he com m and bar. msoBarNoVerticalDock The user cannot dock t he com m and bar in any vert ical posit ion,
eit her left or right . There are t wo ways a user can delet e your com m and bar regardless of it s Prot ect ion set t ing, even if you have disabled t he View > Toolbars > Cust om ize m enu. Bot h of t hese m et hods provide " back doors" t o display t he Cust om ize dialog. I t is part icularly im port ant t o disable t hese opt ions in dict at or applicat ions where delet ing a cust om com m and bar m ay leave t he user wit h no way t o properly exit t he applicat ion. First , if t he Toolbar List com m and bar is enabled, t he user will be able t o delet e your cust om com m and bar by right - clicking anywhere over t he com m and bar area and select ing Cust om ize from t he short cut m enu. To disable t he Toolbar List com m and bar, execut e t he following line of code:
Application.CommandBars("Toolbar List").Enabled = False
Second, if any em pt y t oolbar docking surface is exposed on screen ( t ypically beyond t he right side of a t oolbar) , t he user can double- click anywhere wit hin t his area and t he Cust om ize dialog will display. There is no way t o direct ly disable t his feat ure, so you m ust indirect ly disable it by ensuring t hat no uncovered t oolbar docking area is left exposed by your applicat ion. The easiest way t o do t his is t o add a nonfunct ional Com m andBarBut t on cont rol ( one wit h no Capt ion or OnAct ion assignm ent ) at t he end of each of your t oolbars and set it t o be wide enough so it will cover t he ent ire t oolbar docking area regardless of t he user's screen resolut ion.
IsTemporary The I sTem porary set t ing applies t o Com m andBar and Com m and BarCont rol obj ect s. I f set t o False, t he specified com m and bar or cont rol will be persist ed bet ween Excel sessions. Set t ing t his propert y t o True causes t he com m and bar or cont rol t o be discarded when t he current session of Excel exit s. The default value for t his set t ing is True. The com m and bar builder will rebuild all com m and bars and cont rols defined in t he t able each t im e your applicat ion runs, so t he occasions when you want your cust om com m and bars or cont rols t o be persist ed bet ween Excel sessions are very rare. Leave t his set t ing blank so t he default value is used unless you have a very good reason t o do ot herwise.
IsEnabled The I sEnabled set t ing applies t o Com m andBar and Com m andBarCont rol obj ect s. This set t ing det erm ines whet her t he com m and bar or cont rol is enabled on st art up. A value of True causes t he com m and bar or cont rol t o be enabled. A value of False causes t he com m and bar or cont rol t o be disabled. Disabled com m and bars will not be visible t o t he user. The I sEnabled propert y overrides t he Visible propert y for com m and bars in t his respect . Disabled cont rols will be visible but will appear grayed out . The default value for t his propert y is True.
OnAction
This set t ing applies t o Com m andBarCont rol obj ect s. I t holds t he nam e of t he procedure t hat will be run by t he cont rol. This procedure m ust be a public procedure locat ed in a st andard code m odule. I f you want t o t rap t he Click or Change event rat her t han assigning a procedure t o t he OnAct ion propert y, you can leave t his set t ing blank. We cover cont rol event t rapping in t he Hooking Com m and Bar Cont rol Event s sect ion lat er in t his chapt er. I f t he Cont rol I D set t ing is anyt hing ot her t han 1, t he OnAct ion set t ing is ignored. You will underst and why t his is t he case when we describe t he Cont rol I D set t ing next .
Control ID This set t ing applies t o Com m andBarCont rol obj ect s. Giving t his set t ing a value of 1 m eans t hat you are creat ing a cust om cont rol whose propert ies are specified by t he rest of t he colum ns in t he t able. Any value ot her t han 1 is int erpret ed as t he I D of a built - in Excel cont rol. I n t hat case, t he built - in cont rol specified by t he Cont rol I D value will be added t o your com m and bar, along wit h it s funct ion and appearance. I f you specify a built - in cont rol using t he Cont rol I D set t ing, t he following com m and bar definit ion t able set t ings will be ignored: OnAct ion Cont rol Type Cont rol St yle Short cut Text St at e List Range List s You can det erm ine t he I D you need t o use in order t o add a built - in cont rol t o your com m and bar in t he following m anner. Assum e you want t o add t he Pr int m enu it em from t he File m enu on t he Worksheet Menu Bar t o your cust om com m and bar. Ent er t he following int o t he VBE I m m ediat e w indow :
?
CommandBars("File").Controls("Print...").ID 4
The I m m ediat e window is covered in m ore det ail in Chapt er 16 VBA Debugging, but for now not e t hat t he ? charact er t ells t he I m m ediat e window t o print t he result of t he expression t hat follows it . I n t his case, t he result is t he num ber 4, shown direct ly below t he expression. This is t he I D of t he Pr int cont rol. To add t his cont rol t o your cust om com m and bar, you j ust place 4 in t he Cont rol I D colum n of t he appropriat e row in t he com m and bar definit ion t able.
N OTE A quirk in t he Office Com m andBars obj ect m odel enables you t o access t he t op- level m enus of t he Excel m enu bar as Com m andBar obj ect s in t heir own right . I f you loop t he cont ent s of t he Com m andBars collect ion, you won't find t hese m enus cont ained in it , but you can access t hem using t he synt ax shown above j ust t he sam e.
Control Type This set t ing applies t o Com m andBarCont rol obj ect s. I t is used t o specify what t ype of cont rol you want . This set t ing m ust be one of t he following msoControlType enum erat ion m em bers: msoControlButton This is a m enu or t oolbar but t on t hat sim ply execut es t he specified OnAct ion procedure when it is clicked. The m aj orit y of Com m andBarCont rols t hat you see on Excel's m enus and t oolbars are t his t ype of cont rol. msoControlComboBox This is a com bo box cont rol t hat enables users t o eit her select an ent ry from a predefined list or ent er a new value of t heir choosing. An exam ple of t his t ype of cont rol is t he Zoom com bo box on t he St andard t oolbar. You can select from a predefined list of zoom values or supply your own. msoControlDropdown This cont rol looks exact ly like t he msoControlComboBox cont rol but t he only opt ion allowed is t o select an it em from t he predefined list . msoControlEdit This is an edit box cont rol t hat allows t he user t o ent er an arbit rary t ext value. msoControlPopup This t ype of cont rol is used t o creat e a subm enu cont aining a list of one or m ore m enu it em s. All of t he t op- level m enus on t he Excel m enu bar are of t ype msoControlPopup. Rat her t han doing anyt hing direct ly, t hey j ust display t heir associat ed subm enu. This is t he on ly cont rol t ype t hat can display a subm enu. For cust om cont rols, t he default value for t his set t ing is msoControlButton. We exam ine how you use each of t hese cont rol t ypes in m ore det ail in t he Put t ing I t All Toget her sect ion lat er in t his chapt er .
N OTE I f you look in t he VBE Obj ect Browser, you will discover t hat t here are anywhere from 21 t o 27 different msoControlType enum erat ion m em bers depending on t he version of Excel you are using. Unfort unat ely, you are lim it ed t o one of t he five m em bers list ed above when building cust om Com m andBarCont rols. Cont rols wit h som e of t he ot her enum erat ion m em ber t ypes can be added t o a cust om com m and bar by adding a built - in cont rol of t hose t ypes ( by adding t he Borders but t on from t he Form at t ing t oolbar, for exam ple, whose t ype is msoControlSplitButtonPopup) .
Som e of t he msoControlType enum erat ion m em bers sim ply haven't been im plem ent ed. msoControlOCXDropdown is one exam ple.
Control Style This set t ing applies t o Com m andBarCont rol obj ect s. I t specifies t he visual layout of t he cont rol. This set t ing does not apply t o t he Cont rol Types msoControlEdit or msoControlPopup. I t applies t o t he ot her cont rol t ypes in t he following m anner: msoControlButton Must be one of t he following msoButtonStyle enum erat ion m em bers: msoButtonAutomatic This is t he default value for cont rols of t ype msoControlButton. For a m enu it em , t his is equivalent t o msoButtonIconAndCaption. For a t oolbar but t on, t his is equivalent t o msoButtonIcon. msoButtonCaption This st yle displays only t he capt ion assigned t o t he cont rol. Any icon assigned t o t he cont rol is ignored. msoButtonIcon This st yle is a bit confusing. I t displays only t he icon for t oolbar but t ons and only t he ca pt ion for m enu it em s. I f no icon is specified for a t oolbar but t on wit h t his st yle, a blank but t on will be creat ed. msoButtonIconAndCaption This st yle displays t he icon and places t he capt ion t o t he right of t he icon for bot h m enu it em s and t oolbar but t ons. msoButtonIconAndCaptionBelow This st yle has exact ly t he sam e effect as msoButtonIconAndCaption for m enu it em s. For t oolbar but t ons it displays t he capt ion cent ered below t he icon. msoButtonIconAndWrapCaption This st yle is sim ilar t o t he msoButtonIconAndCaption st yle, but it wraps long capt ions t o t he right of t he icon inst ead of displaying t hem on a single line. This st yle gives very poor visual result s when used wit h m enu it em s, so we recom m end against using it for t hat t ype of cont rol. msoButtonIconAndWrapCaptionBelow For t oolbar but t ons t his st yle is sim ilar t o t he msoButtonIconAndCaptionBelow st yle except t hat long capt ions are wrapped beneat h t he but t on icon. For m enu it em s t his st yle gives exact ly t he sam e poor result s as t he msoButtonIconAndWrapCaption st yle, so we recom m end against using it for t hat t ype of cont r ol. msoButtonWrapCaption This st yle is sim ilar t o t he msoButtonCaption st yle in t hat it ignores any icon assigned t o t he cont rol. The difference is t hat it wraps long capt ions rat her t han displaying t hem on a single line. msoControlComboBox an d msoControlDropdown Must be one of t he following msoComboStyle enum erat ion m em bers: msoComboNormal This is t he default value for cont rols of t ype msoControlComboBox and
msoControlDropdown. I t sim ply displays t he cont rol wit h no capt ion. msoComboLabel This st yle displays t he capt ion direct ly t o t he left of t he com bo box or drop- down cont rol. Because t he Cont rol St yle set t ing does not apply t o cont rols of t ype msoControlPopup, you cannot m odify t he default appearance of t his t ype of cont rol. There is no workaround for t his lim it at ion. Because t he Cont rol St yle set t ing does not apply t o cont rols of t ype msoControlEdit, you cannot provide a capt ion for t hese cont rols. I f your edit box cont rol is locat ed on a t oolbar, you can work around t his lim it at ion by adding a nonfunct ional msoControlButton wit h t he msoButtonCaption st yle t hat displays t he capt ion you desire direct ly t o t he left of t he edit box cont rol. We dem onst rat e t his workaround in t he Put t ing I t All Toget her sect ion lat er in t his chapt er.
Face ID This set t ing applies t o Com m andBarCont rol obj ect s. I t specifies t he icon t hat will be associat ed wit h t he cont rol. The Face I D set t ing can be specified in one of t hree ways:
1 . You can use t he icon from a built - in cont rol by specifying it s I D as t he value for t he Face I D set t ing. 2 . You can use a cust om icon by specifying it s nam e. This icon m ust be a 16x16 pixel graphic t hat has been placed on t he wksCom m andBars worksheet . We will dem onst rat e t his in t he Put t ing I t All Toget her sect ion lat er in t he chapt er. 3 . When operat ing under Excel 2002 or higher, you have t he opt ion of specifying an icon and a m ask. This m et hod provides significant ly superior visual result s for t hese versions of Excel. Bot h t he icon and t he m ask m ust be 16x16 pixel graphics t hat are locat ed on t he wksCom m andBars worksheet . The icon and m ask pict ure nam es m ust be ent ered t oget her int o t he Face I D cell separat ed by a forward slash ( / ) charact er. We discuss t his m et hod in m ore det ail in t he Loading Cust om I cons from Files and Hooking Com m and Bar Cont rol Event s sect ions lat er in t he chapt er. Not e t hat t he icon pict ure from t he icon/ m ask pair will aut om at ically be used in versions of Excel earlier t han Excel 2002, m aking m et hod t hree equivalent t o m et hod t wo when running on down- level versions of Excel. The m ost im port ant charact erist ic of a cust om icon is t hat it s background appear t ransparent when applied t o a cont rol. To use cust om icons under Excel 97/ 2000, you m ust use t he Set Transparent Color cont rol from t he Pict ure t oolbar t o specify a t ransparent background color for t he single pict ure t hat will becom e t he cust om icon for your cont rol. This m et hod is illust rat ed by t he before and aft er pict ures shown in Figure 8- 4 and Figure 8- 5 .
Figu r e 8 - 4 . An I con Pict u r e Be for e Se t t in g t h e Tr a n spa r e n t Ba ck gr ou n d
Figu r e 8 - 5 . An I con Pict u r e Aft e r Se t t in g t h e Tr a n spa r e n t Ba ck gr ou n d
Just place t he Set Transparent Color cont rol cursor over t he color in your pict ure t hat you want t o becom e t ransparent and click. That color will disappear and t he background will show t hrough it . We have dem onst rat ed t his exam ple wit h a pict ure t hat has been enlarged enough so t hat you can see what is happening. This is t ypically t he way you would perform t his operat ion in t he real world as well. A 16x16 pict ure is t oo sm all t o accurat ely point at t he desired background color when you are dealing wit h a com plex icon. Just st ret ch t he pict ure out t o a size large enough so t hat you can see what you are doing, set t he t ransparent background color, t hen revert t he pict ure t o it s original size in t he following m anner:
1 . Right - click over t he pict ure and choose Form at Pict ure from t he short cut m enu. 2 . I n t he Form at Pict ure dialog, select t he Size t ab. 3 . Click t he Reset but t on in t he lower- right corner of t he Size t ab.
Excel 97/ 2000, Excel 2002 and Excel 2003 all use different UI drawing t echniques, so pict ure backgrounds set t o be t ransparent in one version of Excel will not appear t ransparent in ot her versions. As you will see in t he sect ion on Loading Cust om I cons from Files lat er in t he chapt er, t wo new m et hods were added t o t he Com m andBarBut t on obj ect in Excel 2002 t hat enable you t o load pict ures direct ly int o t he cont rol in a m anner t hat is independent of t heir appearance on t he w or k sheet . For t his reason we recom m end t hat you use t he icon/ m ask m et hod described in point 3 above for creat ing cust om icons in Excel 2002 and higher, while set t ing t he t ransparent background color of t he icon pict ure using Excel 97/ 2000. This gives t he com m and bar builder an appropriat ely form at t ed icon for use in Excel 97/ 2000. I t will aut om at ically use m et hod t wo under t hese versions of Excel. You do not have t o do anyt hing special t o t he m ask pict ure, because t he m ask is not used under Excel 97/ 2000.
Begin Group This set t ing applies t o Com m andBarCont rol obj ect s. I t is a True/ False set t ing t hat specifies whet her a separat or bar will be placed above or t o t he left of t he specified cont rol depending on cont ext . A value of True adds a separat or bar. False is t he default value for t his set t ing; so if False is specified or t his set t ing is left blank, no separat or bar will be creat ed.
Before This set t ing applies t o Com m andBarCont rol obj ect s. I t is used t o posit ion a new cont rol before an exist ing cont rol. I f t he cont rols are arranged horizont ally, t he Before set t ing will place your cont rol t o t he left of t he cont rol specified. I f t he cont rols are arranged vert ically, t he Before set t ing will place your cont rol above t he cont rol specified. The exist ing cont rol can be specified eit her by it s nam e or I D num ber. This set t ing is not required and t here is no default value. I f t his set t ing is left blank or if t he cont rol specified by t his set t ing cannot be locat ed, t his set t ing will be ignored. I n t his case, t he cont rol will be added at t he syst em default posit ion, which is at t he end of t he current set of cont rols on t he sam e level. The Before set t ing is t ypically used t o posit ion cont rols t hat are being added t o built - in Excel com m and bars.
Tooltip This set t ing applies t o Com m andBarCont rol obj ect s. I t specifies t he t ext t hat will be cont ained in t he yellow t oolt ip m essage t hat displays when t he user hovers t he m ouse point er over t he cont rol. This set t ing does not apply t o cont rols of t ype msoControlPopup or t o any child cont rol of an
msoControlPopup cont rol. I f t he Toolt ip set t ing is not specified, t he syst em default value displayed in t he t oolt ip is t he capt ion of t he cont rol.
Shortcut Text This set t ing applies t o Com m andBarCont rol obj ect s. I t displays t he keyboard short cut t hat will be assigned t o t he cont rol direct ly t o t he right of t he capt ion for t he cont rol. This set t ing applies only t o cont rols of t ype msoControlButton t hat are child cont rols of an msoControlPopup cont rol ( t hat is, m enu it em s on a subm enu) . Specifying t his set t ing does not act ually assign t he specified keyboard short cut t o t he m acro assigned t o t he OnAct ion set t ing of t he cont rol. You m ust do t his separat ely in your own code. The Short cut Text set t ing is not required and t here is no default value.
Tag This set t ing applies t o Com m andBarCont rol obj ect s. I t enables you t o st ore St ring dat a for your own use. The Tag set t ing does not m odify t he appearance or funct ion of t he cont rol in any way. One com m on use of t his set t ing is t o different iat e am ong cont rols t hat have been assigned t he sam e OnAct ion procedure. Assum e you have assigned t he OnAct ion set t ings of t hree cont rols t o t he sam e procedure. You t hen assign t he values 1, 2 and 3 t o t he Tag set t ings of t he first , second and t hird cont rols, respect ively. When t he specified OnAct ion procedure is called by one of t hese cont rols, you can ident ify t he cont rol t hat called it in t he m anner shown in List ing 8- 1 and condit ionally redirect program execut ion based on t he result .
List in g 8 - 1 . D ist in gu ish in g Con t r ols Usin g t h e Ta g Se t t in g Public Sub MyProcedure() Dim lControl As Long ' Retrieve the Tag value of the control that ' called this procedure. lControl = CLng(CommandBars.ActionControl.Tag) Select Case lControl Case 1 ' Perform the action for control 1. Case 2 ' Perform the action for control 2. Case 3 ' Perform the action for control 3. End Select End Sub
As you will see in t he Hooking Com m and Bar Cont rol Event s sect ion lat er in t he chapt er, t he Tag set t ing is also used t o specify t he cust om cont rols whose event s you want t o hook as a group.
Parameter This set t ing applies t o Com m andBarCont rol obj ect s. I t is funct ionally ident ical t o t he Tag set t ing. I t is a place for t he program m er t o st ore St ring dat a t hat will not have any effect on t he appearance or funct ion of t he cont rol.
State This set t ing applies t o Com m andBarCont rol obj ect s. I t enables you t o creat e checked m enu it em s or depressed t oolbar but t ons. The value for t his set t ing m ust be one of t he following msoButtonState enum erat ion m em bers: msoButtonDown For t oolbar but t ons t his creat es t he visual effect of t he but t on being depressed. For m enu it em s, t he effect depends on whet her t here is an icon displayed wit h t he m enu it em . For m enu it em s wit h icons, t he icon will appear depressed in a fashion very sim ilar t o t he effect on t oolbar but t ons. For m enu it em s wit hout an icon, a depressed check m ark will be added t o t he left of t he m enu capt ion. msoButtonMixed For all current versions of Excel, t his value is indist inguishable from msoButtonDown. I t is included in t he com m and bar builder in case it becom es support ed for som e different purpose in a fut ure version of Excel. msoButtonUp This is t he default value for t his set t ing. A St at e value of msoButtonUp has no effect on t he appearance of newly creat ed cont rols. This value only com es int o play as a way t o rem ove t he effect of t he msoButtonDown or msoButtonMixed values. The St at e set t ing applies only t o cust om cont rols of t ype msoControlButton. Keep in m ind t hat t he St at e propert y is a dynam ic propert y of t he cont rol. The com m and bar builder will creat e t he cont rol wit h what ever init ial St at e value you specify, but aft er t he cont rol has been creat ed you will need t o writ e cust om code t o m odify t he St at e propert y appropriat ely in response t o user act ions. We dem onst rat e t his in t he Put t ing I t All Toget her sect ion lat er in t he chapt er.
ListRange and Lists These set t ings apply t o Com m andBarCont rol obj ect s of t ype msoControlComboBox or msoControlDropdown. We discuss t hese set t ings t oget her because t hey are, in effect , a single set t ing t hat specifies t he list t o be loaded int o a com bo box or drop- down cont rol. The purpose of t hese set t ings is as follows: List Ra n ge This value specifies t he address of t he range on t he wksCom m andBars worksheet t hat holds t he list t o be loaded int o t he cont rol. The specified range m ust be locat ed in t he
List s colum n. Like all ot her set t ings described so far, t he List Range set t ing m ust be locat ed on t he sam e row as t he cont rol t o which it applies. List s This set t ing is a list of values t hat will be loaded int o t he cont rol. This is t he only set t ing t hat does not have t o be locat ed on t he sam e row as t he cont rol it applies t o, and as we discuss below, it should not be locat ed in rows t hat are part of any com m and bar or cont rol definit ion. You should always place your list s below t he last com m and bar definit ion in t he t able. By doing t his, if you need t o insert or delet e rows in a com m and bar definit ion, you will not inadvert ent ly alt er one of your list s. For sim ilar reasons, use a dynam ic form ula t o creat e t he list address value for t he List Range set t ing. I f you hard- code t he list address and t hen insert or delet e rows in t he com m and bar definit ion t able, t he list address will no longer be valid. Use of a dynam ically adj ust ing form ula t o specify t he list locat ion solves t his problem . An exam ple of a dynam ically adj ust ing List address form ula is shown here:
=ADDRESS(ROW(Y48),COLUMN(Y48))&":"&ADDRESS(ROW(Y53),COLUMN(Y53))
This form ula indicat es t hat t he List s set t ing is locat ed in colum n Y of t he com m and bar definit ion worksheet . The list current ly st art s in row 48 of t hat colum n and ends in row 53 of t hat colum n. Not ice, however, t hat t his form ula uses relat ive addresses. I f you insert or delet e rows above t he list , t he addresses specified in t he form ula will aut om at ically adj ust t o t he new locat ion of t he list . The result of t his form ula, as displayed in t he List Range cell and as read by t he com m and bar builder code, is shown here:
$Y$48:$Y$53
The com m and bar builder will use t he range specified by t his address as t he list t o be loaded int o t he cont rol defined in t he row in which t he List Range set t ing is locat ed. Not e t hat t he last com m and bar definit ion in t his exam ple ends at row 47. Therefore, placing t he list below row 47 ensures t hat any subsequent insert ions or delet ions wit hin t he com m and bar definit ion t able will not have any effect on t he cont ent s of t he list . We dem onst rat e t his t echnique in t he Put t ing I t All Toget her sect ion lat er in t he chapt er.
Post Mortem Alt hough it seem s as if every com m and bar and cont rol propert y under t he sun has been covered here, we're not even close. Only t he m ost frequent ly used propert ies have been included in t he com m and bar builder. Ot her propert ies t hat you m ay find an occasional need for can always be m anually coded int o your applicat ion. Exam ples of propert ies t hat are not included in t he com m and bar builder are t he Height and RowI ndex propert ies of t he Com m andBar obj ect , because in our experience t hese propert ies are rarely used when building com m and bars. Propert ies of t he Com m andBarCont rol obj ect t hat are not support ed include t he Descript ionText propert y, because it sim ply duplicat es t he purpose of t he
Tag and/ or Param et er propert ies and t he HyperLinkType propert y, because it is so rarely used. There are also dynam ic propert ies such as I sEnabled and St at e whose init ial values are set by t he com m and bar builder, but whose subsequent values m ust be m anaged by cust om code in your applicat ion as t he need t o change t hem arises.
Putting It All Together I n t his sect ion we creat e several com m on variat ions of com m and bars t hat are not associat ed wit h any applicat ion. We use t hese exam ples t o dem onst rat e m any of t he set t ings described in t he previous sect ion. I n t his exam ple we use t he version of t he com m and bar builder code t hat has been int egrat ed wit h t he error handling syst em t o be described in Chapt er 12 VBA Error Handling . Because t he prim ary focus of t his sect ion is creat ing a valid com m and bar definit ion t able, t his should not cause any problem s. All of t he error handling t echniques you'll see in t he code for t his exam ple are fully explained in Chapt er 12 VBA Error Handling . The error handled com m and bar builder is t he one we st rongly recom m end you use in your own proj ect s, so bear wit h us if you're looking at t he code and it isn't clear what all of it does. I f you do use t his version of t he com m and bar builder, you will need t o im port one addit ional m odule besides t he t hree list ed at t he beginning of t he chapt er. This is t he MErrorHandler m odule cont aining all t he error handling code referenced by t he com m and bar builder. The code for t his exam ple is locat ed in t he Com m andBarDem o.xls workbook t hat can be found on t he CD in t he \ Concept s\ Ch08Advanced Com m and Bar Handling folder. We st rongly recom m end t hat you open t his workbook and follow along while you read t his sect ion. The com m and bar definit ion t able is physically t oo large t o enable us t o use screen shot s t o display all of t he im port ant inform at ion we'll be discussing. The com m and bars in t he Com m andBarDem o.xls workbook will be built aut om at ically whenever t he workbook is opened. Three t ypes of cust om com m and bar are dem onst rat ed:
1 . A cust om m enu cont aining subm enus added t o t he exist ing worksheet m enu bar 2 . A cust om t oolbar 3 . A cust om right - click com m and bar Figure 8- 6 shows t he com plet e set of cust om com m and bars creat ed by t he Com m andBarDem o.xls workbook. To rem ove t he cust om com m and bars and close t he workbook, select Cust om > Exit from t he Excel m enu.
Figu r e 8 - 6 . Th e Cu st om Com m a n d Ba r s Ex a m ple [View full size image]
Adding a Custom Menu with Submenus to the Worksheet Menu Bar Figure 8- 7 shows t he com m and bar definit ion t able layout required t o add a new t op- level m enu t o t he Worksheet Menu Bar.
Figu r e 8 - 7 . Addin g a Cu st om M e n u t o t h e W or k sh e e t M e n u Ba r
Not ice t hat Worksheet Menu Bar has been ent ered for t he Com m and Bar Nam e set t ing value. The com m and bar building code will recognize t hat t his com m and bar already exist s and will add all subsequent ly defined cont rols t o t he exist ing com m and bar. As explained in t he sect ion on t he Com m and Bar Nam e set t ing above, t here can be no ent ries bet ween t he nam e of t he com m and bar and t he St op keyword.
The new t op- level m enu we are adding t o t he Worksheet Menu Bar is locat ed in t he first of t he t hree Cont rol Capt ion colum ns. I t s nam e is Cust om . The am persand charact er in front of t he first let t er of t he cont rol capt ion indicat es t he let t er C will be t he short cut key for t his m enu. We have also used t he Before set t ing ( not shown here) in t he com m and bar definit ion t able t o specify t hat t he Cust om m enu will be added direct ly t o t he left of t he Window m enu. This is t he st andard posit ion for cust om m enus added t o t he Worksheet Menu Bar. All subsequent cont rols will be const ruct ed as child m enus at som e level below t he Cust om m enu. You can verify t his is t he case because t he Cont rol Capt ion colum n ent ries visually display t he m enu hierarchy. I n t his case, no Cont rol Capt ion ent ries exist below t he Cust om ent ry, so all subsequent cont rols m ust be children of t his m enu. The full Cust om m enu is shown in Figure 8- 8 .
Figu r e 8 - 8 . Th e Cu st om M e n u
Unless specified ot herwise, all t he cust om cont rols in t his exam ple have t heir OnAct ion set t ings assigned t o t he GeneralDem o procedure shown in List ing 8- 2 . When a m enu or t oolbar but t on wit h t his OnAct ion assignm ent is clicked, a m essage box cont aining t he capt ion of t he cont rol t hat called t he procedure will display. This is a very sim ple exam ple of how you can use a single procedure t o handle m ult iple cont rols.
List in g 8 - 2 . Th e Ge n e r a lD e m o Pr oce du r e Public Sub GeneralDemo() Dim sCaller As String sCaller = CommandBars.ActionControl.Caption MsgBox sCaller & " was clicked.", vbInformation, gsAPP_TITLE End Sub
The Cust om m enu has t hree subm enus:
1 . Su bm e n u 1 We use t his cont rol t o dem onst rat e t he use of t he St at e set t ing. This cont rol is init ially creat ed wit h it s St at e set t o msoButtonDown, as dem onst rat ed by t he depressed checkm ark displayed t o t he left of t he cont rol in Figure 8- 8 . Subm enu 1 is assigned t o a
special procedure called St at eDem o t hat t oggles t he St at e of t he cont rol bet ween msoButtonDown and msoButtonUp each t im e it is clicked. The St at eDem o procedure is shown in List ing 8- 3 .
List in g 8 - 3 . Th e St a t e D e m o Pr oce du r e Public Sub StateDemo() Dim ctlCaller As CommandBarButton Dim sMsg As String Set ctlCaller = CommandBars.ActionControl If ctlCaller.State = msoButtonDown Then ' Switch to msoButtonUp. ctlCaller.State = msoButtonUp sMsg = "The state has been switched to up." Else ' Switch to msoButtonDown. ctlCaller.State = msoButtonDown sMsg = "The state has been switched to down." End If MsgBox sMsg, vbInformation, gsAPP_TITLE End Sub
2 . Su bm e n u 2 We use t his cont rol t o dem onst rat e t hird- level subm enus ( oft en referred t o as subm enu it em s) . Subm enu 2 is a cont rol of t ype msoControlPopup. As shown in Figure 8- 7 , it cont ains t hree subm enu it em s. Each of t hese subm enu it em s has had it s FaceI D set t ing assigned t o t he I D num ber of a built - in cont rol. As explained in t he sect ion on t he FaceI D set t ing above, assigning t he I D num ber of a built - in cont rol t o t he FaceI D set t ing of a cust om cont rol enables you t o give your cont rol t he appearance of t he built - in cont rol wit hout t aking on any of it s ot her charact erist ics. 3 . Ex it This is j ust a plain vanilla subm enu cont rol t hat is used t o exit t he dem o applicat ion. I t s OnAct ion set t ing is assigned t o t he AppExit procedure, which init iat es shut down of t he applicat ion.
Adding a Custom Toolbar Figure 8- 9 shows t he com m and bar definit ion t able layout required t o creat e our cust om t oolbar. Because t he t oolbar requires only a single level of Cont rol Capt ion set t ings, we've used t he freeze panes feat ure t o scroll t he t able over t o show som e of t he addit ional set t ings used t o creat e t he cont rols on t he t oolbar.
Figu r e 8 - 9 . Addin g a Cu st om Toolba r
[View full size image]
I f you're following along wit h t he act ual workbook exam ple, you'll see t hat t he row in t he com m and bar definit ion t able used t o specify t he t oolbar required only a single set t ing: Visible = TRUE. This lack of a requirem ent for explicit set t ings in m ost of t he com m and bar definit ion t able colum ns is pervasive and it represent s one of t he prim ary st rengt hs of t his syst em . The default values for all set t ings are designed t o be t he values you will use m ost frequent ly when building com m and bars and cont rols. Therefore, t he com m and bar definit ion t able requires very few ent ries for t he m aj orit y of com m and bars and cont rols you will build wit h it . Just be sure you underst and what t he default ent ries are. I f you're ever unsure, read t he cell com m ent at t he t op of t he colum n. Any default s will be list ed t here as well as any ot her crit ical inform at ion required t o use t he set t ing cont rolled by t hat colum n correct ly. The t oolbar built by t he com m and bar definit ion in Figure 8- 9 is shown in Figure 8- 10.
Figu r e 8 - 1 0 . Th e Cu st om Toolba r [View full size image]
The cont rols on t he Cust om Toolbar are used t o dem onst rat e a num ber a feat ures of t he com m and bar builder: N e w a n d Ope n As you can see in Figure 8- 9 , t here is no Cont rol I D value specified for t he New or Open but t ons. This m eans t hey will be creat ed as cust om cont rols. Bot h OnAct ion set t ings use t he GeneralDem o procedure shown in List ing 8- 2 and t heir FaceI D set t ings ( not shown) are t he I D num bers of t he built - in New and Open cont rols, respect ively. What m akes t hese cont rols different from your average t oolbar but t ons are t heir Cont rol St yle set t ings. As shown in Figure 8- 9 , t hese cont rols have a Cont rol St yle of msoButton IconAndCaption. Rat her t han sim ply displaying an icon alone, t his st yle displays t he cont rols
wit h t heir capt ions t o t he right of t heir icons. This is a som ewhat unusual but oft en very useful display t echnique when you have room on your t oolbar t o use it . The m eanings of t oolbar but t ons oft en t end t o be obscure based on t he icon alone. Adding a capt ion t o a t oolbar but t on can m ake it s purpose m uch m ore obvious. Sa ve a n d Pr in t As you can see in Figure 8- 9 , t hese but t ons have Cont rol I D values ot her t han 1. I n t his case t he Cont rol I D values are t he I D values for t he built - in Save and Print cont rols, respect ively. This m eans t he built - in Save and Print cont rols have been added t o our cust om t oolbar wit h all of t heir appearance and funct ion int act . These cont rols have no OnAct ion set t ing because t hey would ignore it . Clicking eit her one of t hese cont rols causes t hem t o perform t he sam e act ion t hey would perform as built - in cont rols. Te x t Box La be l a n d Te x t Box This set of cont rols is used t o dem onst rat e how you can add a t ext box cont rol t o your t oolbar and fake a capt ion for it by placing a nonfunct ional Com m andBarBut t on cont rol wit h t he desired capt ion t o t he left of t he t ext box cont rol. I n Figure 8- 9 you can see t hat t he OnAct ion set t ings of t he t ext box cont rol is assigned t o a special- purpose procedure called HandleText Box, shown in List ing 8- 4 .
List in g 8 - 4 . Th e H a n dle Te x t Box Pr oce du r e Public Sub HandleTextBox() Dim ctlCaller As CommandBarControl Set ctlCaller = CommandBars.ActionControl MsgBox "You entered: '" & ctlCaller.Text & "'." End Sub
This procedure perform s exact ly t he sam e funct ion as t he GeneralDem o procedure except it displays t he value ent ered in t he t ext box cont rol. D r opdow n This cont rol dem onst rat es how t o add a cont rol of t ype msoControlComboBox or msoControlDropdown t o your t oolbar. This specific exam ple dem onst rat es an msoControlDropdown cont rol, but t he t wo t ypes of cont rols are alm ost ident ical. Everyt hing you see in t his exam ple can be applied t o a cont rol of t ype msoControlComboBox. I n Figure 8- 9 , not e t hat t he Cont rol St yle set t ing for t he drop- down cont rol has a value of 1. This is t he value of t he msoComboLabel st yle. I t causes t he capt ion of t he drop- down t o display t o t he left of t he cont rol it self. The OnAct ion set t ing of t he drop- down cont rol is assigned t o t he cust om HandleDropDown procedure shown in List ing 8- 5 .
List in g 8 - 5 . Th e H a n dle D r opD ow n Pr oce du r e Public Sub HandleDropDown() Dim ctlCaller As CommandBarComboBox Set ctlCaller = CommandBars.ActionControl MsgBox "You selected: '" & ctlCaller.Text & "'."
End Sub
Aft er you select an ent ry from t he drop- down list , t his procedure displays t he list it em you select ed. Pr e viou s a n d N e x t These cont rols dem onst rat e how t o apply cust om icons t o your cont rols. I n Figure 8- 11, you can see t he Previous and Next Cont rol Capt ion set t ings, t heir Face I D set t ings t hat specify nam ed pict ures and t he t wo pict ures nam ed by t he Face I D set t ings, which happen t o be locat ed in t he Begin Group colum n. ( They can be locat ed anywhere on t he wksCom m andBars worksheet .)
Figu r e 8 - 1 1 . Cu st om I con s on t h e Toolba r
I n Figure 8- 11 t he pict ure for t he Previous but t on is select ed and you can see t hat it s nam e, as shown in t he nam e box direct ly t o t he left of t he form ula bar, is exact ly t he sam e as t he nam e specified for t he Previous cont rol in it s Face I D set t ing ( cell P20) . For sim plicit y's sake we have not dem onst rat ed t he icon/ m ask com binat ion in t his Face I D exam ple. The Face I D set t ings shown above will work in any version of Excel, alt hough not necessarily wit h opt im al appearance. We cover t he m ore com plex icon/ m ask m et hod of set t ing t he Face I D propert y in t he sect ions t hat follow.
Adding a Custom Right-Click Command Bar Figure 8- 12 shows t he com m and bar definit ion t able layout required t o creat e our cust om right click com m and bar. I f you exam ine t he Posit ion set t ing for t his com m and bar, you will see t hat it s value has been set t o msoBarPopup.
Figu r e 8 - 1 2 . Addin g a Cu st om Righ t - Click Com m a n d Ba r
The only cont rol set t ing on t his com m and bar wit h values assigned t o it is t he Cont rol I D set t ing. This is because all of t he cont rols on our cust om right - click m enu are built - in cont rols. Not j ust built - in cont rols by appearance, but t he act ual built - in cont rols specified by t he I D num bers in t he Cont rol I D colum n, including all of t heir feat ures and at t ribut es. The right - click com m and bar built by t his com m and bar t able definit ion is shown in Figure 8- 13.
Figu r e 8 - 1 3 . Th e Cu st om Righ t - Click Com m a n d Ba r
We have replaced t he built - in com m and bar norm ally invoked by right - clicking over a worksheet cell wit h our cust om right - click com m and bar. This was accom plished using t he workbook- level Sheet BeforeRight Click event , as shown in List ing 8- 6 .
List in g 8 - 6 . Th e W or k book _ Sh e e t Be for e Righ t Click Eve n t H a n dle r Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, _ ByVal Target As Range, Cancel As Boolean)
Dim cbrBar As CommandBar ' Only attempt to display the custom right-click ' command bar if it exists. On Error Resume Next Set cbrBar = Nothing Set cbrBar = Application.CommandBars("Custom Popup") On Error GoTo 0 If Not cbrBar Is Nothing Then ' Show our custom right-click command bar. cbrBar.ShowPopup ' Cancel the default action of the right-click. Cancel = True End If End Sub
As Figure 8- 13 shows, t he cont rols on our cust om right - click m enu are behaving exact ly like t heir corresponding Excel cont rols. Because we have not yet copied anyt hing, bot h t he Past e and Past e Special m enus are disabled. The Copy m enu is enabled, allowing us t o copy a range. The Zoom cont rol is enabled because we can m odify t he zoom at any t im e. We do not need t o t ake any act ion t o m ake t hese cont rols behave in t his m anner because we are using t he built - in cont rols t hem selves. Excel ensures t hey behave appropriat ely. I n t he Hooking Com m and Bar Cont rol Event s sect ion, you will see how we can have t he best of bot h worlds. By hooking com m and bar cont rol event s, we can ut ilize t he appearance of built - in cont rols provided by Excel while also having t hem run t he cust om code of our choice rat her t han perform t heir norm al act ions.
Loading Custom Icons from Files As explained in t he sect ion on t he FaceI D set t ing above, each version of Excel from 2000 onward uses a slight ly different UI drawing t echnique. This m eans cust om icons st ored as pict ures on a worksheet will only appear correct ly in t he version of Excel in which t hey were opt im ized. For applicat ions running under Excel 2002 or higher, t here is a second way t o apply cust om icons t o com m and bar cont rols t hat elim inat es t he problem s associat ed wit h t he different drawing t echniques used by different versions of Excel. The com m and bar builder support s t his m et hod aut om at ically, but in t his sect ion we will explain how t o use it m anually so you can t ake advant age of it in an applicat ion t hat does not use t he com m and bar builder. Beginning wit h Office XP, t wo new propert ies were added t o t he Com m andBarBut t on obj ect . These t wo propert ies enable you t o load icons direct ly int o t he cont rol. The Pict ure propert y specifies t he bit m ap t o be used as t he foreground of t he icon and t he Mask propert y specifies t he bit m ap t hat indicat es which areas of t he icon should be rendered as t ransparent background. I n t he next sect ion, we show you how t o creat e t hese bit m aps. Because t his m et hod requires t wo bit m ap files for each icon, if you have a large num ber of cust om icons it can becom e unwieldy due t o t he num ber of files you m ust dist ribut e. I n Chapt er 20 Com bining Excel and Visual Basic 6 we show how you can package up all of your cust om icons int o a single DLL resource file. Alt ernat ively, if you are using t he com m and bar builder, you can host bot h t he icon and m ask pict ure files on t he wksCom m andBars worksheet .
Creating Bitmap Files for Icons and Masks I f you plan t o creat e cust om icons on a regular basis, it probably m akes sense t o purchase a special- purpose icon creat ion program . For t he occasional cust om icon, however, every version of Windows com es wit h a perfect ly serviceable icon creat ion t ool: Microsoft Paint . The t ype of file you will need t o creat e for use as a Com m andBarBut t on icon is a 16x16 pixel 16color bit m ap. The icon file will cont ain t he art ist ic foreground pict ure you t hink of as your icon. The m ask file is an overlay for t he icon file in which everyt hing foreground is colored black and everyt hing you want t o be t ransparent background is colored whit e. To creat e a cust om icon, open Microsoft Paint . Paint will open wit h a blank default im age canvas. Select I m age > At t ribut es from t he Paint m enu. I n t he At t ribut es dialog, set Widt h and Height t o 16, Unit s t o Pixels and Colors t o Color. These set t ings are shown in Figure 8- 14.
Figu r e 8 - 1 4 . Th e I con At t r ibu t e s Se t t in gs
Click t he OK but t on on t he At t ribut es dialog and select View > Zoom > Cust om from t he Paint m enu. Select 800% as your Zoom t o set t ing. The result ing im age canvas is shown in Figure 8- 15.
Figu r e 8 - 1 5 . Th e Bla n k I con Ca n va s in Pa in t
You are now ready t o draw your icon. Drawing icons well requires m uch pract ice. The appearance of a 16x16 pixel im age at 800 percent m agnificat ion is oft en not hing like it s appearance at norm al size. We've provided a sim ple cust om icon im age you can use for t est ing purposes. This icon is called Arrows.bm p and is locat ed on t he CD in t he \ Concept s\ Ch08Advanced Com m and Bar Handling folder. This icon is shown loaded int o Paint in Figure 8- 16.
Figu r e 8 - 1 6 . Th e Ar r ow s I con
The m ask file for t he arrows icon is called ArrowsMask.bm p and is locat ed in t he sam e CD folder as t he Arrows.bm p file. As you can see in Figure 8- 17, t his file sim ply replaces t he blue foreground color of t he original icon wit h black. When loaded int o t he Com m andBarBut t on, t he areas of t he icon corresponding t o t he black areas of t he m ask will display, while t he areas of t he icon corresponding t o t he whit e areas of t he m ask will be t ransparent .
Figu r e 8 - 1 7 . Th e M a sk File for t h e Ar r ow s I con
Using Bitmap Files as CommandBarButton Icons Now t hat we have our icon and m ask bit m aps, it 's a sim ple m at t er t o apply t hem t o a Com m andBarBut t on. The procedures shown in List ing 8- 7 build a com m and bar wit h a single but t on t hat uses our cust om arrow icon and m ask. You can find t his code in t he LoadPict ureAndMask.xls workbook locat ed on t he CD in t he \ Concept s\ Ch08Advanced Com m and Bar Handling folder. Not e t hat t his exam ple only works in Excel 2002 or lat er.
List in g 8 - 7 . Addin g a Cu st om I con t o a Com m a n dBa r Bu t t on Public Sub CreateBar() Dim cbrBar As CommandBar Dim ctlControl As CommandBarButton Dim sPath As String ' Make sure any previously created version of our demo ' command bar is deleted.
RemoveBar ' We're assuming that the bitmap files used to create the ' custom icon are located in the same path as this workbook. sPath = ThisWorkbook.Path If Right$(sPath, 1) "\" Then sPath = sPath & "\" ' Create a toolbar-type command bar. Set cbrBar = CommandBars.Add("Demo", msoBarTop, False, True) cbrBar.Visible = True ' Add the command bar button control. Set ctlControl = cbrBar.Controls.Add(msoControlButton) ' Load the foreground bitmap file. ctlControl.Picture = LoadPicture(sPath & "Arrows.bmp") ' Load the mask bitmap file. ctlControl.Mask = LoadPicture(sPath & "ArrowsMask.bmp") End Sub Public Sub RemoveBar() On Error Resume Next CommandBars("Demo").Delete End Sub
To creat e t he com m and bar but t on wit h t he cust om icon, run t he Creat eBar procedure. To rem ove t he dem o com m and bar and it s but t on, run t he Rem oveBar procedure. The result ing cust om icon appears as shown in Figure 8- 18.
Figu r e 8 - 1 8 . Th e Ar r ow s Cu st om I con
Hooking Command Bar Control Events I n Office 2000, Microsoft added t he Click event t o t he Com m and BarBut t on obj ect and t he Change event t o t he Com m andBarCom boBox obj ect t o provide an event - based m echanism for working wit h com m and bar cont rols in addit ion t o t he OnAct ion propert y. The prim ary reason for t his was t o allow cont rols t o be used by non- VBA code, such as COM Add- ins, which were int roduced at t he sam e t im e. There are, however, subt le differences in t he behavior of a cont rol when using an event hook com pared t o using t he OnAct ion propert y. We can exploit t his behavior t o our advant age in VBA.
Why Use an Event Hook Set t ing t he OnAct ion propert y is usually t he easiest opt ion for cust om com m and bar cont rols; when t he cont rol is clicked, t he procedure specified by t he OnAct ion propert y is called. Using an event hook enables us t o int eract wit h cont rols in t hree addit ional ways t hat cannot be accom plished using t he OnAct ion propert y: 1 . Hook t he Click event s of t he built - in cont rols, giving us a Before_xxx event for any cont rol t hat we hook. 2 . Hook t he Click event s of bot h built - in and cust om cont rols in ot her Office applicat ions, including t he VBE. For exam ple, if we're using Excel t o aut om at e Word, our Excel code can respond t o t he user clicking a cont rol in Wordeit her a built - in Word cont rol or one we have creat ed. 3 . Hook t he Click event s of bot h built - in and cust om cont rols from out side VBA, such as when aut om at ing Excel from Visual Basic ( Chapt er 20 Com bining Excel and Visual Basic 6) , wit hin COM Add- ins ( Chapt er 21 Writ ing Add- ins wit h Visual Basic 6) or in VSTO solut ions ( Chapt er 22 Using VB.NET and t he Visual St udio Tools for Office) .
What Can an Event Hook Do When hooking cust om cont rols, event hooks behave exact ly like OnAct ion procedures. They run when t he cont rol is clicked. When hooking built - in cont rols, however, t he event hook is called be for e t h e bu ilt - in pr oce ss, effect ively giving us a Before_xxx event in which t o run any code we want and/ or cancel t he built - in processing. For exam ple, in Chapt er 6 Dict at or Applicat ions we used an OnKey assignm ent t o ensure t hat t he Ct rl+ V keyboard short cut past ed only values int o our dat a- ent ry form s. I f we also hook t he Edit > Past e m enu, we could check t o see whet her t he cell being past ed int o was wit hin our dat a- ent ry form . I f so, we would replace it wit h a Past e Special > Values and cancel Excel's default past e behavior using t he CancelDefault argum ent supplied by t he Click event procedure. Ot herwise we
would let Excel perform a norm al past e operat ion. Wit hin t he Click event procedure we could also use t he Application.OnTime m et hod t o run a rout ine im m ediat ely aft er Excel has done it s norm al process, effect ively giving us an Aft er_xxx event . This m et hod will only work wit hin an Excel- based add- in. We can also use event hooks t o im plem ent cust om cont rols t hat are enabled and disabled aut om at ically as t he environm ent changes. For exam ple, t he built - in Past e Values cont rol is only enabled when t here is som et hing on t he clipboard t o past e and a cell select ed t o past e int o. We can use event hooks t o creat e our own cust om but t ons t hat are enabled and disabled in t he sam e way. All we need t o do is creat e a copy of t he Past e Values cont rol, give it a different icon, add our own code t o t he click event and cancel t he default behavior. Because our cust om cont rols are based on a built - in cont rol, Excel handles t heir enable/ disable behavior for us. We will dem onst rat e t his in The Past e Special Com m and Bar exam ple below.
The Importance of the Tag Property When you use a Wit hEvent s obj ect declarat ion t o hook a Com m andBarBut t on or Com m andBarCom boBox, you're not act ually hooking t hat specific inst ance of t he cont rol, but rat her t hat cont rol I D for built - in cont rols or t hat I D/ Tag propert y com binat ion for cust om cont rols. This m eans when you hook one of t he built - in cont rols, your event will be fired whenever a n y inst ance of t hat cont rol is clicked, a big advant age over having t o search for every inst ance of t he cont rol and hook each one ( including having t o det erm ine whet her t he user has dragged a new one on t o t heir t oolbar at som e point during your applicat ion's operat ion) . Cust om cont rols wit h an I D of 1 and no Tag assignm ent are t reat ed as unique, individual cont rols for t he purposes of a Wit hEvent s assignm ent . This is a safet y m echanism built in t o t he event hooking syst em . I f hooking any cust om cont rol wit h an I D/ Tag com binat ion of 1/ < blank> m eant hooking all cust om cont rols wit h t hat I D/ Tag com binat ion you m ight be hooking a very large num ber of cont rols indeed, including m any t hat didn't even belong t o your applicat ion. To t ake advant age of m ult iple sim ult aneous event hooks for our cust om cont rols, we need t o assign t he sam e Tag value t o all of t he cust om cont rols we want t o hook t oget her. We can t hen use t he Param et er value of t he Ct rl argum ent passed t o t he event t o ident ify which cont rol was clicked and decide what t o do wit h it . We can also have a cust om cont rol em ulat e t he enabled/ disabled behavior of a built - in cont rol aut om at ically. We do t his by assigning t he I D value of t he built - in cont rol whose behavior we want t o em ulat e t o t he Cont rol I D value of our cust om cont rol. We t hen give t hat cont rol a unique Tag value and set up t he event hook. Excel will m anage t he enabled/ disabled behavior of our cust om cont rol but t he cont rol will run t he code we assign t o it in t he event handler. This is act ually j ust a special case of hooking a built - in cont rol. And because we are hooking t he I D of a built - in cont rol, t hat built - in cont rol will also act ivat e our event handler. We can use t he Tag value of t he Ct rl argum ent passed t o t he event procedure t o det erm ine whet her t he event was fired by our cust om cont rol or t he built - in cont rol whose behavior our cust om cont rol em ulat es. I f t here is no Tag, we know t he built - in cont rol called t he event . I n t his case we sim ply do not hing and allow Excel t o perform it s default process for t he cont rol. I f t he Tag propert y is set , we know our cust om cont rol called t he event . I n t his case we cancel Excel's default act ion and run our own code in it s place.
I f we want t o have m ult iple cust om cont rols, all wit h t he sam e enabled/ disabled behavior but each wit h different act ions, we can give t hese cont rols t he sam e Cont rol I D and Tag values so t hey all fire t he sam e event hook, t hen use t he Param et er value t o uniquely ident ify each cont rol in order t o condit ionally execut e t he correct code for it in t he event handler. This is all very confusing, but it will becom e clear once you see t he exam ple in t he next sect ion.
The Paste Special Command Bar Aft er you have copied a range, Excel provides a num ber of very useful past e special opt ions t hat are buried under t he Edit > Past e Special m enu. Built - in t oolbar but t ons are provided for t wo com m only used past e special opt ions: Past e Values and Past e Form at t ing. What we want , however, is a t oolbar t hat exposes a ll of t he m ost com m only used past e special opt ions. All of t he but t ons on t his t oolbar should have t he sam e enabled/ disabled behavior exhibit ed by t he built - in Past e Values but t on, but t hey should run t he operat ion- specific code t hat we assign t o t hem . I n t his sect ion we t ake advant age of com m and bar cont rol event hooking t o creat e t his t oolbar. The workbook t hat im plem ent s t his exam ple is called Past eSpecialBar.xls and is locat ed on t he CD in t he \ Concept s\ Ch08Advanced Com m and Bar Handling folder. We st rongly recom m end t hat you open t his workbook and exam ine it while you read t his sect ion.
The Paste Special Toolbar Definition The first st ep in creat ing our Past e Special t oolbar is t o writ e t he correct definit ion for it in t he com m and bar definit ion t able. The com plet e com m and bar definit ion t able for t his t oolbar is t oo wide t o fit wit hin a single screen shot , so we show a series of screen shot s t hat ut ilize t he Excel freeze panes feat ure t o display several of t he m ost im port ant sect ions of t he definit ion t able. Showing t he ent ire com m and bar definit ion t able for our Past e Special t oolbar would require m ore screen shot s t han we have room for, so please follow along wit h t he exam ple workbook provided on t he CD. I n Figure 8- 19 we show t he basic st ruct ure and definit ion of t he Past e Special t oolbar and it s cont rols.
Figu r e 8 - 1 9 . Th e Ba sic Pa st e Spe cia l Toolba r D e fin it ion
Our Past e Special t oolbar will be const ruct ed as an msoBarFloating Com m andBar wit h seven cont rols of t ype msoControlButton. Not e t hat all of t he cont rols on t he t oolbar have been assigned t he sam e built - in Cont rol I D value 370. This num ber is t he I D of t he built - in Past e Values Com m andBarBut t on. What we are doing is creat ing seven ident ical copies of t he built - in Past e Values cont rol t hat we will lat er m odify t o perform different act ions. We do t his because even aft er our m odificat ions, Excel will t reat t hese cont rols as if t hey were copies of t he Past e Values cont rol for t he purpose of enabling and disabling t hem . This is exact ly what we want . I n Figure 8- 20 we dem onst rat e how we are set t ing t he appearance of our seven cont rols.
Figu r e 8 - 2 0 . Th e Pa st e Spe cia l Toolba r Fa ce I D Assign m e n t s [View full size image]
Let 's st art wit h t he Values but t on. Because all of t he cont rols on our t oolbar are copies of t he built in Past e Values cont rol, and t his cont rol is act ually included on our t oolbar, we don't have t o specify anyt hing for it s Face I D set t ing. I t will t ake on t he appearance of t he Past e Values cont rol by default . Next , look at t he Face I D set t ing for t he Form at t ing cont rol. Because Excel provides a built in Past e Form at t ing cont rol, we can j ust use it s Face I D rat her t han having t o creat e a cust om icon for our Form at t ing cont rol. For all t he ot her cont rols on our t oolbar, we have provided cust om icons and m asks. The purpose and usage of icons and m asks has been covered ext ensively in t he Face I D t opic and t he Loading Cust om I cons From Files sect ion above, so we do not repeat t hat inform at ion here. What we do is describe how t his feat ure has been im plem ent ed on t he com m and bar definit ion t able shown in Figure 8- 20. The pict ures for t he icons and m asks have been placed in t he unused Cont rol St yle colum n. This colum n is unused because t he default value msoButtonIcon is exact ly what we want for our cont rols. These pict ures could t heoret ically be locat ed anywhere on t he wksCom m andBars worksheet , but for ease of m aint enance we recom m end you place your icon and m ask pict ures on t he sam e row as t he cont rol t o which t hey apply and as close as possible t o t he Face I D colum n in which t hey are nam ed. Alt hough it is som ewhat difficult t o different iat e when looking at a blackand- whit e screen shot , in all cases t he icon is t he pict ure on t he left and t he m ask is t he pict ure on t he right .
I f you exam ine t he value of t he Face I D set t ing for each of t he cont rols ut ilizing an icon and m ask you will see t hat it consist s of t he icon pict ure nam e and t he m ask pict ure nam e separat ed by a forward slash ( / ) charact er. For Excel versions 2002 and higher, bot h of t hese pict ures will be used t o creat e t he icon for t he cont rol. For Excel versions 2000 and lower, only t he icon pict ure will be used and it s appearance on t he cont rol will be exact ly t he sam e as it s appearance on t he worksheet . Therefore, if your applicat ion will be run on Excel 97 or Excel 2000, you should set t he t ransparent background color of t he icon pict ure in Excel 2000 and save t he workbook in t hat version of Excel, as we have done here. Figure 8- 21 shows t he Tag and Param et er set t ings for our cont rols.
Figu r e 8 - 2 1 . Th e Pa st e Spe cia l Toolba r Ta g a n d Pa r a m e t e r Assign m e n t s
Except for t he Values cont rol, all t he cont rols have been assigned t he sam e Tag set t ing value. This is what allows all of t hese cont rols' event s t o be t rapped by t he sam e event handler. The Tag value is not required for t he Values cont rol because it is a built - in copy of t he Excel Past e Values cont rol. Because all of our cont rols are copies of t his cont rol, our event handler will t rap it s event aut om at ically. I n t he event code t hat we show in a m om ent , event calls from t he built - in Past e Values cont rol are ignored and Excel is allowed t o handle t hem as if t hey had not been t rapped at all. When t he Past e Special t oolbar is first creat ed, t here is not hing on t he clipboard and t herefore all of t he cont rols are in t he disabled st at e, as shown in Figure 8- 22.
Figu r e 8 - 2 2 . Th e Pa st e Spe cia l Toolba r w it h All Con t r ols D isa ble d
Achieving t his effect requires absolut ely no work on our part . By using t he Past e Values cont rol as t he basis for all t he cust om cont rols on t he Past e Special t oolbar, Excel m anages enabling and disabling t he cont rols appropriat ely for us. Aft er a range has been copied, Excel aut om at ically enables all of our cont rols, as shown in Figure 8- 23.
Figu r e 8 - 2 3 . Th e Pa st e Spe cia l Toolba r w it h All Con t r ols En a ble d
Now let 's look at t he code required t o m anage t hese cont rols. A Wit hEvent s class m odule called CCont rolEvent s is used t o t rap t he event s for our cont rols. A reference t o t his class m ust be creat ed in and held by a global variable so t hat event t rapping cont inues t hroughout t he life of our applicat ion. Therefore, we m ust add t he following obj ect variable declarat ion t o t he MGlobals m odule of our exam ple workbook:
Public gclsControlEvents As CControlEvents
I t seem s obvious, but bears m ent ioning, t hat t he global class variable cannot be inst ant iat ed unt il aft er we have built t he com m and bars specified in t he com m and bar definit ion t able. Ot herwise, t here would be no cont rols t o hook. Bot h of t hese t asks are accom plished in t he Aut o_Open procedure, a fragm ent of which is shown in List ing 8- 8 . As in our Put t ing I t All Toget her exam ple, t he version of t he com m and bar builder code used here has been int egrat ed wit h t he error handling syst em t o be described in Chapt er 12 VBA Error Handling .
List in g 8 - 8 . I n st a n t ia t in g t h e Eve n t H a n dle r in t h e Au t o_ Ope n Pr oce du r e ' Initialize global variables. InitGlobals ' Build the custom command bars specified in the ' wksCommandBars table. If Not bBuildCommandBars() Then Err.Raise glHANDLED_ERROR ' Instantiate the control event handler class variable. Set gclsControlEvents = New CControlEvents
The com plet e code from t he CCont rolEvent s class m odule t hat act ually t raps and handles t he cont rol event s is shown in List ing 8- 9 .
List in g 8 - 9 . Th e CCon t r olEve n t s Cla ss M odu le Private WithEvents mctlPasteSpecial As Office.CommandBarButton Private Sub Class_Initialize() ' Find and hook one of our custom buttons. ' The Click event will fire when *any* of the controls with ' the same ID and Tag are clicked, as well as when the ' built-in control whose ID we're using is clicked. ' We've given all our controls the same ID and Tag, so ' we're handling the click events for all our controls ' using a single hook and event handler. Set mctlPasteSpecial = _ CommandBars.FindControl(Tag:=gsMENU_TAG) End Sub Private Sub Class_Terminate() Set mctlPasteSpecial = Nothing End Sub Private Sub mctlPasteSpecial_Click( _ ByVal Ctrl As Office.CommandBarButton, _ CancelDefault As Boolean) Dim uPasteType As XlPasteType ' This is called for all instances of the built-in ' Paste Special > Values button as well as our custom ' Paste Special buttons, so check if it's one of ours. ' If the button is not one of ours, we'll do nothing ' and Excel will perform its normal action for that ' button. If Ctrl.Tag = gsMENU_TAG Then ' It is one of ours, so set the appropriate paste type. Select Case Ctrl.Parameter Case gsMENU_PS_ALL uPasteType = xlPasteAll Case gsMENU_PS_FORMULAS uPasteType = xlPasteFormulas Case gsMENU_PS_VALUES uPasteType = xlPasteValues Case gsMENU_PS_FORMATS uPasteType = xlPasteFormats Case gsMENU_PS_COMMENTS uPasteType = xlPasteComments
Case gsMENU_PS_VALIDATION uPasteType = 6 ' xlPasteValidation in 2002+ Case gsMENU_PS_COLWIDTHS uPasteType = 8 ' xlPasteColumnWidths in 2002+ End Select ' If the paste special doesn't succeed, fail silently. On Error Resume Next Selection.PasteSpecial uPasteType On Error GoTo 0 ' We handled the event, so cancel its default behavior. CancelDefault = True End If End Sub
When t he global gclsCont rolEvent s class variable is inst ant iat ed by t he Aut o_Open procedure, t he first t hing t hat happens is t he Class_I nit ialize event fires. This event locat es a single inst ance of a cont rol on our Past e Special t oolbar and assigns it t o t he int ernal Wit hEvent s class variable. As we have explained previously, t his is enough t o cause a ll t he cont rols on our t oolbar t o be hooked by our event handler ( as well as any built - in Past e Values cont rols on which our cust om cont rols are based) . Because Excel is m anaging whet her our cont rols are enabled or disabled, when our m ct lPast eSpecial_Click event does fire, we know t he user has clicked one of our cont rols a n d t here is som et hing on t he clipboard t hat can pot ent ially be past ed. The first it em of business is t hen t o det erm ine whet her t he cont rol t hat fired t he click event is one of our cust om cont rols. We do t his by com paring t he Tag propert y exposed by t he Ct rl argum ent t o t he Tag value t hat we have assigned t o each of our cust om cont rols. I f t he Tag propert y of t he Ct rl argum ent doesn't m at ch t he Tag value we have assigned t o our cust om cont rols, we know t hat a built - in Excel cont rol fired t he event procedure. I n t his case we j ust exit t he procedure wit hout doing anyt hing. This allows Excel t o perform t he default act ion for t hat built - in cont rol, which is t he behavior we want . I f t he Tag propert y of t he cont rol t hat fired t he event m at ches t he Tag value we assigned t o our cust om cont rols, we know we're dealing wit h one of our cust om cont rols. I n t his case we cont inue processing. The act ion we t ake depends on t he value of t he Param et er propert y of t he cont rol t hat fired t he event . The Param et er propert y is used t o dist inguish am ong our cust om cont rols because t he Cont rol I D and Tag propert ies are ident ical for all of t hem . This is what allows t hem all t o fire t he sam e event procedure. I n t his case, t he Param et er value is used t o specify t he t ype of past e special operat ion t hat should be perform ed. Wit hin t he event procedure we convert t he Param et er value int o one of t he xlPasteType enum erat ion values. Aft er we have t he correct past e special enum erat ion value, we at t em pt t o perform t he specified operat ion. This past e special operat ion is wrapped in On Error Resume Next/On Error GoTo 0 so no error will be generat ed if t he past e special operat ion being at t em pt ed is not valid for t he current version of Excel or t he cont ent s of t he clipboard. We explain t he use of t he various perm ut at ions of t he On Error st at em ent in m ore det ail in Chapt er 12 VBA Error Handling.
Practical Example Because we have not yet int roduced t he t opic of error handling, t he com m and bar builder t hat we int egrat e int o t he sam ple applicat ion at t his point does not m ake use of any error handling t echniques. This is not t he preferred m et hod, but we are using it t o avoid confusing t he addit ion of an aut om at ed com m and bar builder, discussed in t his chapt er, wit h t he addit ion of error handling, covered in Chapt er 12 VBA Error Handling . The t oolbar for our PETRAS add- in is a very sim ple one com pared t o t he exam ples t hat we've seen already. We show it again in Figure 8- 24 t o refresh your m em ory.
Figu r e 8 - 2 4 . Th e PETRAS Add- in Toolba r [View full size image]
PETRAS Timesheet As Figure 8- 24 shows, all five but t ons on t he t oolbar are cust om but t ons and all of t hem use built in Excel com m and bar cont rol Face I D values, so t here is no need t o at t ach ext ernal pict ures t o t hem . A part ial view of t he com m and bar definit ion t able is shown in Figure 8- 25. We've frozen panes at colum n B and scrolled t he right pane t o colum n O t o show som e of t he m ore int erest ing cont rol set t ings. To see t he ent ire t able, j ust set t he I sAddin propert y of t he Pet rasAddin.xla workbook t o False so t he wksCom m andBars worksheet is visible in t he Excel user int erface.
Figu r e 8 - 2 5 . Th e PETRAS Add- in Com m a n d Ba r D e fin it ion Ta ble
The code wit hin t he add- in looks exact ly like it did when we last saw it except for t he addit ion of t wo code m odules: MCom m andBars and MPast ePict ure. These hold t he code t hat reads t he com m and bar definit ion t able and builds t he com m and bars it specifies. The procedure call used t o creat e t he com m and bar in t he Aut o_Open procedure is exact ly t he sam e as before. The difference is now it calls t he BuildCom m andBars procedure in t he MCom m andBars m odule inst ead of our previous, hard- coded com m and bar building procedure of t he sam e nam e t hat was locat ed in t he MSyst em Code m odule. There has been one sim ple change in t he Aut o_Close procedure. Rat her t han rem oving our cust om t oolbar wit h t he line:
Application.CommandBars(gsBAR_TOOLBAR).Delete
we are now calling t he MCom m andBars procedure designed t o work backward t hrough t he com m and bars t able and rem ove t he com m and bars and cont rols it specifies:
ResetCommandBars
We've done t his for illust rat ion purposes only. I n t he sim ple case of a single, fully cust om t oolbar, t he first line of code is t he m ore efficient m et hod for rem oval. When you begin building com plex applicat ions t hat use a com binat ion of m odified built - in and fully cust om ized com m and bars, however, you will find it m uch easier t o let t he com m and bar builder rem ove your applicat ion's com m and bars based on t he com m and bar definit ion t able t hat defined t hem in t he first place. A sum m ary of t he changes m ade t o t he PETRAS t im esheet applicat ion t o im plem ent t he t abledriven com m and bar builder is shown in Table 8- 2.
Ta ble 8 - 2 . Ch a n ge s t o t h e PETRAS Tim e sh e e t Applica t ion for Ch a pt e r 8 M odu le
Pr oce du r e
Ch a n g e
MCom m andBars ( new m odule)
New m odule cont aining t he com m and bar building code.
MPast ePict ure ( new m odule)
New m odule t o support t he com m and bar builder. Used t o add a pict ure t o and ret rieve a pict ure from t he clipboard.
M odu le
Pr oce du r e
Ch a n g e
MOpenClose
Aut o_Open
The BuildCom m andbars procedure is now called from t he new MCom m andBars m odule inst ead of MSyst em Code.
MSyst em Code
BuildCom m andBar s
This procedure was rem oved because t he t ask of building t he com m and bars is now handled by t he t able- driven com m and bar builder.
PETRAS Reporting Previous versions of t he PETRAS report ing applicat ion have had a very sim ple m enu st ruct ure, lit t le m ore t han t he usual File > New, Open, Close, Save and Exit m enus and a Window m enu t o swit ch bet ween result s workbooks. When we're displaying a result s workbook, however, we would really like t o provide m ost ( but not all) of Excel's built - in m enus, t o enable our users t o work direct ly wit h t he workbook. Adding t he com m and bar builder t o t he applicat ion m akes t his a t rivial t ask of including t he appropriat e built - in m enu I Ds. I n t he definit ion t able shown in Figure 8- 26, for exam ple, we've been able t o include Excel's ent ire Edit m enu ( and all it s subm enus) j ust by specifying it s cont rol I D of 30003.
Figu r e 8 - 2 6 . Th e PETRAS Re por t in g Com m a n d Ba r D e fin it ion Ta ble [View full size image]
I f you look at t he OnAct ion and Cont rol I D colum ns of t he t able, you'll see t hat we have been able t o add lot s of very rich funct ionalit y t o our applicat ion j ust by borrowing Excel's st andard m enus. I n fact , all of t hese feat ures have been added wit hout us having t o writ e a n y code t o im plem ent t hem !
Application Contexts As dict at or applicat ions becom e m ore and m ore com plex, we need an easier way t o handle t he enabling and disabling of t he m enu it em s t han coding t hem individually. One approach is t o int roduce t he concept of an applicat ion cont ext , which is an ident ifier t o specify what part of t he applicat ion is being displayed. Typical cont ext s in Excel dict at or applicat ions include t he following: Ba ck dr op The st at ic backdrop sheet is being displayed, so alm ost all m enus not relat ed t o beginning work or exit ing t he applicat ion are disabled. D a t a En t r y We're in a dat a- ent ry worksheet , so a lim it ed set of edit ing m enus are enabled Re su lt s We're in a result s workbook, so all t he edit ing and form at t ing m enus are enabled. We can specify t he cont ext s in which each m enu it em ( or an ent ire popup t oolbar) should be enabled by list ing t he applicable cont ext s in t he Param et er colum n of t he definit ion t able. I n t he PETRAS report ing applicat ion, we're only using t he Backdrop and Result s cont ext s. Because t he cont ext is usually det erm ined by t he worksheet current ly being displayed, we can use t he applicat ion WindowAct ivat e event t o t rigger t he enabling/ disabling by using code like t hat shown in List ing 8- 10.
List in g 8 - 1 0 . Th e Code t o I m ple m e n t Applica t ion Con t e x t s Private Sub mxlApp_WindowActivate(ByVal Wb As Workbook, _ ByVal Wn As Window) 'Set the correct context, depending if we have a results 'workbook or not. If IsResultsWorkbook(Wb) Then EnableDisableMenus gsCONTEXT_RESULTS Else EnableDisableMenus gsCONTEXT_BACKDROP End If End Sub 'Enable/disable menu items, depending on the 'application context. Sub EnableDisableMenus(ByVal sContext As String) Dim cbCommandbar As CommandBar
On Error Resume Next 'Enable/disable key menu items, by calling the 'EnableDisableMenuBar procedure, which recursively operates 'on all Menu items in the structure EnableDisableMenuBar Application.CommandBars(gsMENU_BAR), _ sContext, "" 'Enable/disable all the toolbars For Each cbCommandbar In Application.CommandBars If cbCommandbar.Type msoBarTypeMenuBar Then cbCommandbar.Enabled = (sContext = gsCONTEXT_RESULTS) End If Next 'Enable/disable the associated shortcut keys If sContext = gsCONTEXT_RESULTS Then Application.OnKey "^s" Application.OnKey "^S" Else Application.OnKey "^s", "" Application.OnKey "^S", "" End If End Sub 'Recursive routine to process the menu bar hierarchy, 'enabling/disabling items based on their context. Private Sub EnableDisableMenuBar(cbBar As CommandBar, _ sContext As String, sBarContext As String) Dim ctlControl As CommandBarControl On Error Resume Next 'Loop through all the controls on this bar For Each ctlControl In cbBar.Controls If TypeOf ctlControl Is CommandBarPopup Then 'If it's a popup, recurse down to process its menus EnableDisableMenuBar ctlControl.CommandBar, _ sContext, ctlControl.Parameter ElseIf ctlControl.Parameter = "" Then 'If the control doesn't have a parameter, use the 'commandbar's parameter. This allows us to add entire 'Excel built-in commandbars to our app, without 'specifying every menu item on them ctlControl.Enabled = InStr(1, sBarContext, _ sContext) > 0 Else 'Otherwise enable/disable the bar
ctlControl.Enabled = InStr(1, ctlControl.Parameter, _ sContext) > 0 End If Next End Sub
Adding t he t able- driven com m and bar builder required a num ber of relat ively m inor changes t hroughout t he PETRAS report ing applicat ion, det ailed in Table 8- 3.
Ta ble 8 - 3 . Ch a n ge s t o t h e PETRAS Re por t in g Applica t ion for Ch a pt e r 8 M odu le
Pr oce du r e
Ch a n g e
MOpenClose
Aut o_Open
Set init ial applicat ion cont ext at end of rout ine.
MCom m andBar s
Replaced t he ent ire m odule wit h t he t able- driven com m and bar builder.
MPast ePict ure ( new m odule)
New m odule t o support t he com m and bar builder. Used t o add a pict ure t o and ret rieve a pict ure from t he clipboard.
MGlobals
Added const ant s for applicat ion cont ext s.
MEnt r yPoint s
MenuWindow Select
We were using t he Param et er t o t est for t he PETRAS Backdrop m enu it em . Changed t o use t he capt ion inst ead, as t he Param et er is now used for t he applicat ion cont ext .
CAppEv ent Handler
m x lApp_Window Act iv at e
I dent ify t he applicat ion cont ext and pass it t o EnableDisableMenus, as shown in List ing 8- 10.
MSyst em Code
EnableDisableMenus
I m plem ent ed List ing 8- 10, t o enable/ disable t he m enus based on applicat ion cont ext inst ead of hard- coding each m enu it em .
M odu le
Pr oce du r e
Ch a n g e
MSyst em Code
AddToWindow Menu
Set t he Param et er value t o Backdrop,Result s when adding t he workbook window m enu it em s.
MWor k space
Rest or eEx celSet t ings
Moved t he code t o re- enable t he t oolbars t o here, from t he old Rest oreMenus rout ine ( which has been replaced by t he com m and bar builder) .
wksCom m andBars ( new w or k sheet )
New worksheet t o hold t he com m and bar definit ion t able.
Conclusion The purpose of t his chapt er is t o sim plify com m and bar building in your applicat ions t o save t im e and allow you t o focus m ore effort on good design. To assist in t hat effort , we've covered a num ber of best pract ice design principles t hat you should follow. Com m and bars are t he user's ent ry point int o your applicat ion. Make t hem easy t o discover, use and rem em ber. We've int roduced you t o a t able- driven com m and bar building m et hodology t hat will rem ove m ost of t he work associat ed wit h building and m aint aining com m and bars for your applicat ion. We've shown you how t o creat e cust om icon and m ask pict ures t hat enable you t o avoid t he problem s associat ed wit h divergent appearance of cust om com m and bar but t on icons am ong various current versions of Excel. We've also explained how t o hook t he event s generat ed when t he user clicks com m and bar cont rols so t hat you can cont rol t he behavior of t hose cont rols in a m ore granular fashion.
Chapter 9. Understanding and Using Windows API Calls I n t he Program m ing wit h t he Windows API chapt er of our Excel 2002 VBA Program m ers Reference, we approached t he subj ect of using Windows API calls by explaining how t o locat e t he definit ions for various funct ions on t he MSDN Web sit e and t ranslat e t hose funct ions for use in VBA. The idea was t o enable readers t o browse t hrough t he API docum ent at ion and use anyt hing of int erest t hey found. I n realit y, ext rem ely few people use Windows API calls in t hat m anner; indeed, t rying t o include previously unexplored API calls in our Excel applicat ions is very likely t o result in a m aint enance problem , because it 's doubt ful t hat anot her developer will underst and what we were t rying t o do. I nst ead, m ost of us go t o Google and search t he Web or t he newsgroups for t he answer t o a problem and find t hat t he solut ion requires t he use of API calls. ( Searching Google for " Excel Windows API " result s in m ore t han 200,000 Web pages and 19,000 newsgroup post s.) We copy t he solut ion int o our applicat ion and hope it works, usually wit hout really underst anding what it does. This chapt er shines a light on m any of t hose solut ions, explaining how t hey work, what t hey use t he API calls for, and how t hey can be m odified t o bet t er fit our applicat ions. Along t he way, we fill in som e of t he concept ual fram ework of com m on Windows API t echniques and t erm inology. By t he end of t he chapt er, you will be com fort able about including API calls in your applicat ions, underst and how t hey work, accept t heir use in t he exam ple applicat ions we develop in t his book and be able t o m odify t hem t o suit your needs.
Overview When developing Excel- based applicat ions, we can get m ost t hings done by using t he Excel obj ect m odel. Occasionally, t hough, we need som e inform at ion or feat ure t hat Excel doesn't provide. I n t hose cases, we can usually go direct ly t o t he files t hat com prise t he Windows operat ing syst em t o find what we're looking for. The first st ep in doing t hat is t o t ell VBA t he funct ion exist s, where t o find it , what argum ent s it t akes and what dat a t ype it ret urns. This is done using t he Declare st at em ent , such as t hat for Get Syst em Met rics:
Declare Function GetSystemMetrics Lib "user32" _ (ByVal nIndex As Long) As Long
This st at em ent t ells t he VBA int erpret er t hat t here is a funct ion called Get Syst em Met rics locat ed in t he file user32.exe ( or user32.dll, it 'll check bot h) t hat t akes one argum ent of a Long value and ret urns a Long value. Once defined, we can call Get Syst em Met rics in exact ly t he sam e way as if it is t he VBA funct ion:
Function GetSystemMetrics(ByVal nIndex As Long) As Long End Function
The Declare st at em ent s can be used in any t ype of code m odule, can be Public or Privat e ( j ust like st andard procedures) , but m ust always be placed in t he Declarat ions sect ion at t he t op of t he m odule.
Finding Documentation All of t he funct ions in t he Windows API are fully docum ent ed in t he Windows Developm ent / Plat form SDK sect ion of t he MSDN library on t he Microsoft Web sit e, at ht t p: / / m sdn.m icr osoft .com / libr ar y, alt hough t he t erm inology used and t he code sam ples t end t o be t arget ed at t he C+ + developer. A Google search will usually locat e docum ent at ion m ore appropriat e for t he Visual Basic and VBA developer, but is unlikely t o be as com plet e as MSDN. I f you're using API calls found on a Web sit e, t he Web page will hopefully explain what t hey do, but it is a good idea t o always check t he official docum ent at ion for t he funct ions t o see whet her any lim it at ions or ot her rem arks m ay affect your usage. Unfort unat ely, t he MSDN library's search engine is significant ly worse t han using Google t o search t he MSDN sit e. We find t hat Google always gives us m ore relevant pages t han MSDN's search engine. To use Google t o search MSDN, browse t o ht t p: / / w w w .google.com and click t he Advanced Search link. Type in t he search crit eria and t hen in t he Dom ain edit box t ype m sdn.m icrosoft .com
t o rest rict t he search t o MSDN.
Finding Declarations I t is not uncom m on t o encount er code snippet s on t he I nt ernet t hat include incorrect declarat ions for API funct ionssuch as declaring an argum ent 's dat a t ype as I nt eger or Boolean when it should be Long. Alt hough using t he declarat ion included in t he snippet will probably work ( hopefully t he aut hor t est ed it ) , it m ight not work for t he full range of possible argum ent s t hat t he funct ion accept s and in rare cases m ay cause m em ory corrupt ion and dat a loss. The official VBA- friendly declarat ions for m any of t he m ore com m only used API funct ions can be found in t he win32api.t xt file, which is included wit h a viewer in t he Developer Edit ions of Office 972002, Visual Basic 6 and is available for download from ht t p: / / suppor t .m icr osoft .com / ?kbid= 178020. You'll not ice from t he download page t hat t he file hasn't been updat ed for som e t im e. I t t herefore doesn't include t he declarat ions and const ant s added in recent versions of Windows. I f you're using one of t hose newer declarat ions, you'll have t o t rust t he Web page aut hor, exam ine a num ber of Web pages t o check t hat t hey all use t he sam e declarat ion or creat e your own VBA- friendly declarat ion by following t he st eps we described in t he Excel 2002 VBA Program m ers Reference.
Finding the Values of Constants Most API funct ions are passed const ant s t o m odify t heir behavior or specify t he t ype of value t o ret urn. For exam ple, t he Get Syst em Met rics funct ion shown previously accept s a param et er t o specify which m et ric we want , such as SM_CXSCREEN t o get t he widt h of t he screen in pixels or SM_CYSCREEN t o get t he height . All of t he appropriat e const ant s are shown on t he MSDN page for t hat declarat ion. For exam ple, t he Get Syst em Met rics funct ion is docum ent ed at ht t p: / / m sdn.m icr osoft .com / libr ar y/ en- us/ sysinfo/ base/ get syst em m et r ics.asp and shows m ore t han 70 valid const ant s. Alt hough m any of t he const ant s are included in t he win32api.t xt file m ent ioned earlier, it does not include const ant s added for recent versions of Windows. The best way t o find t hese values is by downloading and inst alling t he core Plat form SDK from ht t p: / / w w w .m icr osoft .com / m sdow nload/ plat for m sdk / sdk updat e/ . This includes all t he C+ + header files t hat were used t o build t he DLLs, in a subdirect ory called \ include. The files in t his direct ory can be searched using norm al Windows file searching t o find t he file t hat cont ains t he const ant we're int erest ed in. For exam ple, searching for SM_CXSCREEN gives t he file winuser.h. Opening t hat file and searching wit hin it gives t he following lines:
#define SM_CXSCREEN #define SM_CYSCREEN
0 1
These const ant s can t hen be included in your VBA m odule by declaring t hem as Long variables wit h t he values shown:
Const SM_CXSCREEN As Long = 0 Const SM_CYSCREEN As Long = 1
Som et im es, t he values will be shown in hexadecim al form , such as 0x8000, which can be convert ed t o VBA by replacing t he 0x wit h &h and adding a furt her & on t he end, such t hat
#define KF_UP
0x8000
becom es
Const KF_UP As Long = &h8000&
Understanding Handles Wit hin VBA, we're used t o set t ing a variable t o reference an obj ect using code like
Set wkbBackDrop = Workbooks("Backdrop.xls")
and releasing t hat reference by set t ing t he variable t o Not hing ( or let t ing VBA do t hat for us when it goes out of scope at t he end of t he procedure) . Under t he covers, t he t hing t hat we see as t he Backdrop.xls workbook is j ust an area of m em ory cont aining dat a st ruct ured in a specific way t hat only Excel underst ands. When we set t he variable equal t o t hat obj ect , it is j ust given t he m em ory locat ion of t hat dat a st ruct ure. The Windows operat ing syst em works in a very sim ilar way, but at a m uch m ore granular level; alm ost everyt hing wit hin Windows is m aint ained as a sm all dat a st ruct ure som ewhere. I f we want t o work wit h t he it em t hat is represent ed by t hat st ruct ure ( such as a window) , we need t o get a reference t o it and pass t hat reference t o t he appropriat e API funct ion. These references are known as h a n dle s and are j ust I D num bers t hat Windows uses t o ident ify t he dat a st ruct ure. Variables used t o st ore handles are usually given t he prefix h and are declared As Long. When we ask for t he handle t o an it em , som e funct ionssuch as FindWindowgive us t he handle t o a shared dat a st ruct ure; t here is only one dat a st ruct ure for each window, so every call t o FindWindow wit h t he sam e param et ers will ret urn t he sam e handle. I n t hese cases, we can j ust discard t he handle when we're finished wit h it . I n m ost sit uat ions, however, Windows allocat es an area of m em ory, creat es a new dat a st ruct ure for us t o use and ret urns t he handle t o t hat st ruct ure. I n t hese cases, we m u st t idy up aft er ourselves, by explicit ly t elling Windows t hat we've finished using t he handle ( and by im plicat ion, t he m em ory used t o st ore t he dat a st ruct ure t hat t he handle point s t o) . I f we fail t o t idy up correct ly, each call t o our rout ine will use anot her bit of m em ory unt il Windows crashest his is known as a m e m or y le a k . The m ost com m on cause of m em ory leaks is forget t ing t o include t idy- up code wit hin a rout ine's error handler. The MSDN docum ent at ion will t ell you whet her you need t o release t he handle and which funct ion t o call t o do it .
Encapsulating API Calls Get Syst em Met rics is one of t he few API calls t hat can easily be used in isolat ionit has a m eaningful nam e, t akes a single param et er, ret urns a sim ple result and doesn't require any preparat ion or cleanup. So long as you can rem em ber what SM_CXSCREEN is asking for, it 's ext rem ely easy t o call t his funct ion; GetSystemMetrics(SM_CXSCREEN) gives us t he widt h of t he screen in pixels. I n general pract ice, however, it is a very good idea t o wrap your API calls inside t heir own VBA funct ions and t o place t hose funct ions in m odules dedicat ed t o specific areas of t he Windows API , for t he following reasons: The VBA rout ine can include som e validit y checks before t rying t o call t he API funct ion. Passing invalid dat a t o API funct ions will oft en result in a crash. Most of t he t ext ual API funct ions require st ring variables t o be defined and passed in, which are t hen populat ed by t he API funct ion. Using a VBA rout ine hides t hat com plexit y. Many API funct ions accept param et ers t hat we don't need t o use. A VBA rout ine can expose only t he param et ers t hat are applicable t o our applicat ion. Few API funct ions can be used in isolat ion; m ost require ext ra preparat ory and clean up calls. Using a VBA rout ine hides t hat com plexit y. The API declarat ions t hem selves can be declared Privat e t o t he m odule in which t hey're cont ained, so t hey can be hidden from use by ot her developers who m ay not underst and how t o use t hem ; t heir funct ionalit y can t hen be exposed t hrough m ore friendly VBA rout ines. Som e API funct ions, such as t he encrypt ion or I nt ernet funct ions, require an init ial set of preparat ory calls t o open resources, a num ber of rout ines t hat use t hose resources and a final set of rout ines t o close t he resources and t idy up. Such rout ines are ideally encapsulat ed in a class m odule, wit h t he Class_I nit ialize and Class_Term inat e procedures used t o ensure t he resources are opened and closed correct ly. By using dedicat ed m odules for specific areas of t he Windows API , we can easily copy t he rout ines bet ween applicat ions, in t he knowledge t hat t hey are self- cont ained. When you st art t o include lot s of API calls in your applicat ion, it quickly becom es difficult t o keep t rack of which const ant s belong t o which funct ions. We can m ake t he const ant s m uch easier t o m anage if we encapsulat e t hem in an enum erat ion and use t hat enum erat ion for our VBA funct ion's param et er, as shown in List ing 9- 1 . By doing t his, t he applicable const ant s are shown in t he I nt ellisense list when t he VBA funct ion is used, as shown in Figure 9- 1 . The abilit y t o define enum erat ions was added in Excel 2000.
List in g 9 - 1 . En ca psu la t in g t h e Ge t Syst e m M e t r ics API Fu n ct ion a n d Re la t e d Con st a n t s 'Declare all the API-specific items Private to the module Private Declare Function GetSystemMetrics Lib "user32" _
(ByVal nIndex As Long) As Long Private Const SM_CXSCREEN As Long = 0 Private Const SM_CYSCREEN As Long = 1 'Wrap the API constants in a public enumeration, 'so they appear in the Intellisense dropdown Public Enum SystemMetricsConstants smScreenWidth = SM_CXSCREEN smScreenHeight = SM_CYSCREEN End Enum 'Wrapper for the GetSystemMetrics API function, 'using the SystemMetricsConstants enumeration Public Function SystemMetrics( _ ByVal uIndex As SystemMetricsConstants) As Long SystemMetrics = GetSystemMetrics(uIndex) End Function
Figu r e 9 - 1 . By Usin g t h e En u m e r a t ion , t h e Re le va n t Con st a n t s Appe a r in t h e I n t e llise n se D r op- D ow n
Working with the Screen The procedures included in t his sect ion all relat e t o t he Windows screen and can be found in t he MScreen m odule of t he API Exam ples.xls workbook.
Reading the Screen Resolution The Get Syst em Met rics API funct ion has been used t o illust rat e t he general concept s above. I t can be used t o discover m any of t he sim pler aspect s of t he operat ing syst em , from whet her a m ouse or net work is present t o t he height of t he st andard window t it le bar. By far it s m ost com m on use in Excel is t o find t he screen resolut ion, t o check t hat it is at least a m inim um size ( for exam ple, 800x600) or t o work out which userform t o display if you have different layout s opt im ized for different resolut ions. The code in List ing 9- 2 wraps t he Get Syst em Met rics API funct ion, exposing it as separat e ScreenWidt h and ScreenHeight funct ions.
List in g 9 - 2 . Re a din g t h e Scr e e n Re solu t ion 'Declare all the API-specific items Private to the module Private Declare Function GetSystemMetrics Lib "user32" _ (ByVal nIndex As Long) As Long Private Const SM_CXSCREEN = 0 'Screen width Private Const SM_CYSCREEN = 1 'Screen height 'The width of the screen, in pixels Public Function ScreenWidth() As Long ScreenWidth = GetSystemMetrics(SM_CXSCREEN) End Function 'The height of the screen, in pixels Public Function ScreenHeight() As Long ScreenHeight = GetSystemMetrics(SM_CYSCREEN) End Function
Finding the Size of a Pixel I n general, Excel m easures dist ances in point s, whereas m ost API funct ions use pixels and m any Act iveX cont rols ( such as t he Microsoft Flexgrid) use t wips. A point is defined as being 1/ 72 ( logical) inches, and a t wip is defined as 1/ 20t h of a point . To convert bet ween pixels and point s, we need t o know how m any pixels Windows is displaying for each logical inch. This is t he DPI ( dot s per inch) set by t he user in Cont rol Panel > Display > Set t ings > Advanced > General > Display ,
which is usually set at eit her Norm al size ( 96 DPI ) or Large size ( 120 DPI ) . I n versions of Windows prior t o XP, t his was known as Sm all Font s and Large Font s. The value of t his set t ing can be found using t he Get DeviceCaps API funct ion, which is used t o exam ine t he det ailed capabilit ies of a specific graphical device, such as a screen or print er.
Device Contexts One of t he fundam ent al feat ures of Windows is t hat applicat ions can int eract wit h all graphical devices ( screens, print ers, or even individual pict ure files) in a st andard way. This is achieved by operat ing t hrough a layer of indirect ion called a device cont ext , which represent s a drawing layer. An applicat ion obt ains a reference ( handle) t o t he drawing layer for a specific device ( for exam ple, t he screen) , exam ines it s capabilit ies ( such as t he size of a dot , whet her it can draw curves and how m any colors it support s) , draws ont o t he drawing layer and t hen releases t he reference. Windows t akes care of exact ly how t he drawing layer is represent ed on t he graphical device. I n t his exam ple, we're only exam ining t he screen's capabilit ies. The code t o ret rieve t he size of a pixel is shown in List ing 9- 3 . Rem em ber t hat when adding t his code t o an exist ing m odule, t he declarat ions m ust always be placed at t he t op of t he m odule.
List in g 9 - 3 . Fin din g t h e Size of a Pix e l Private Declare Function GetDC Lib "user32" _ (ByVal hwnd As Long) As Long Private Declare Function GetDeviceCaps Lib "gdi32" _ (ByVal hDC As Long, ByVal nIndex As Long) As Long Private Declare Function ReleaseDC Lib "user32" _ (ByVal hwnd As Long, ByVal hDC As Long) As Long Private Const LOGPIXELSX = 88
'Pixels/inch in X
'A point is defined as 1/72 inches Private Const POINTS_PER_INCH As Long = 72 'The size of a pixel, in points Public Function PointsPerPixel() As Double Dim hDC As Long Dim lDotsPerInch As Long hDC = GetDC(0) lDotsPerInch = GetDeviceCaps(hDC, LOGPIXELSX) PointsPerPixel = POINTS_PER_INCH / lDotsPerInch ReleaseDC 0, hDC End Function
The first t hing t o not ice about t his rout ine is t hat we cannot j ust call Get DeviceCaps direct ly; we need t o give it a handle t o t he screen's device cont ext . This handle is obt ained by calling t he Get DC funct ion, where t he zero param et er convenient ly gives us t he device cont ext for t he screen. We t hen call Get DeviceCaps, passing t he const ant LOGPI XELSX, which asks for t he num ber of pixels per logical inch horizont ally. ( For screens, t he horizont al and vert ical DPI is t he sam e, but it m ight not be for print ers, which is why circles on screen oft en print out as ovals.) Wit h Norm al size chosen, we get 96 dot s per inch. We divide t he 72 point s per inch by t he 96 DPI , t elling us t hat a dot ( t hat is, pixel) is 0.75 point s; so if we want t o m ove som et hing in Excel by one pixel, we need t o change it s Top or Left by 0.75. Wit h Large Size select ed, a pixel is 0.6 point s. Every t im e we use Get DC t o obt ain a handle t o a device cont ext , we use up a sm all am ount of Window's graphical resources. I f we didn't release t he handle aft er using it , we would event ually use up all of Window's graphical resources and crash. To avoid t hat , we have t o be sure t o release any resources we obt ain, in t his case by calling ReleaseDC.
Working with Windows Everyt hing t hat we see on t he screen is eit her a window or is cont ained wit hin a window, from t he Windows deskt op t o t he sm allest popup t oolt ip. Consequent ly, if we want t o m odify som et hing on t he screen, we always st art by locat ing it s window. The windows are organized int o a hierarchy, wit h t he deskt op at t he root . The next level down includes t he m ain windows for all open applicat ions and num erous syst em - relat ed windows. Each applicat ion t hen owns and m aint ains it s own hierarchy of windows. Every window is ident ified by it s window handle, com m only referred t o as h W n d. By far t he best t ool for locat ing and exam ining windows is t he Spy+ + ut ilit y t hat is included wit h Visual St udio. Figure 9- 2 shows t he Spy+ + display for t he window hierarchy of a t ypical Excel session.
Figu r e 9 - 2 . Th e Spy+ + D ispla y of t h e Ex ce l W in dow H ie r a r ch y
Window Classes As well as showing t he hierarchy, t he Spy+ + display shows t hree key at t ribut es for each window: t he handle ( in hexadecim al) , t he capt ion and t he class. Just like class m odules, a window class defines a t ype of window. Som e classes, such as t he Com boBox class, are provided by t he Windows operat ing syst em , but m ost are defined as part of an applicat ion. Each window class is usually associat ed wit h a specific part of an applicat ion, such as XLMAI N being Excel's m ain applicat ion w indow . Table 9- 1 list s t he window classes shown in t he Spy+ + hierarchy and t heir uses, plus som e ot her window classes com m only encount ered during Excel applicat ion developm ent .
Ta ble 9 - 1 . Ex ce l W in dow Cla sse s a n d Th e ir Use s W in dow Cla ss
Usa ge
XLMAI N
The m ain Excel applicat ion window.
EXCEL;
The left half of t he form ula bar, including t he Nam e drop- down.
Com boBox
A st andard Windows com bo box ( in t his case, it 's t he Nam e drop- down) .
EXCEL
0 Then 'Return the login id, stripping off the final vbNullChar UserName = Left$(sBuffer, lStringLength - 1) End If End Function
Buffers Every API funct ion t hat ret urns t ext ual inform at ion, such as t he user nam e, does so by using a buffer t hat we provide. A buffer com prises a St ring variable init ialized t o a fixed size and a Long variable t o t ell t he funct ion how big t he buffer is. When t he funct ion is called, it writ es t he t ext t o t he buffer ( including a final Null charact er) and ( usually) updat es t he lengt h variable wit h t he num ber of charact ers writ t en. ( Som e funct ions ret urn t he t ext lengt h as t he funct ion's result inst ead of updat ing t he variable.) We can t hen look in t he buffer for t he required t ext . Not e t hat VBA st ores st rings in a very different way t han t he API funct ions expect , so whenever we pass st rings t o API funct ions, VBA does som e conversion for us behind t he scenes. For t his t o work properly, we a lw a ys pass st rings by value ( ByVal) t o API funct ions, even when t he funct ion updat es t he st ring. Som e people prefer t o ignore t he buffer lengt h inform at ion, looking inst ead for t he first vbNullChar charact er in t he buffer and assum ing t hat 's t he end of t he ret rieved st ring, so you m ay encount er usage like t hat shown in List ing 9- 11.
List in g 9 - 1 1 . Usin g a Bu ffe r , I gn or in g t h e Bu ffe r Le n gt h Va r ia ble 'Get the user's login ID, without using the buffer length Function UserName2() As String Dim sBuffer As String * 255 GetUserName sBuffer, 255 UserName2 = Left$(sBuffer, InStr(sBuffer, vbNullChar) - 1) End Function
Changing to a UNC Path VBA's int rinsic ChDrive and ChDir st at em ent s can be used t o change t he act ive pat h prior t o using Application.GetOpenFilename, such t hat t he dialog opens wit h t he correct pat h preselect ed. Unfort unat ely, t hat can only be used t o change t he act ive pat h t o local folders or net work folders t hat have been m apped t o a drive let t er. Not e t hat once set , t he VBA CurDir funct ion will ret urn a UNC pat h. We need t o use API funct ions t o change t he folder t o a net work pat h of t he form \ \ server\ share\ pat h, as shown in List ing 9- 12. I n pract ice, t he Set CurDir API funct ion is one of t he few t hat can be called direct ly from your code.
List in g 9 - 1 2 . Ch a n gin g t o a UN C Pa t h Private Declare Function SetCurDir Lib "kernel32" _ Alias "SetCurrentDirectoryA" _ (ByVal lpPathName As String) As Long 'Change to a UNC Directory
Sub ChDirUNC(ByVal sPath As String) Dim lReturn As Long 'Call the API function to set the current directory lReturn = SetCurDir(sPath) 'A zero return value means an error If lReturn = 0 Then Err.Raise vbObjectError + 1, "Error setting path." End If End Sub
Locating Special Folders Windows m aint ains a large num ber of special folders t hat relat e t o eit her t he current user or t he syst em configurat ion. When a user is logged in t o Windows wit h relat ively low privileges, such as t he basic User account , it is highly likely t hat t he user will only have full access t o his personal folders, such as his My Docum ent s folder. These folders can usually be found under C: \ Docum ent s and Set t ings\ UserNam e, but could be locat ed anywhere. We can use an API funct ion t o give us t he correct pat hs t o t hese special folders, using t he code shown in List ing 9- 13. Not e t hat t his list ing cont ains a subset of all t he possible folder const ant s. The full list can be found by searching MSDN for " CSI DL Values." The not able except ion from t his list is t he user's Tem p folder, which can be found by using t he Get Tem pPat h funct ion. List ing 9- 13 includes a special case for t his folder, so t hat it can be obt ained t hrough t he sam e funct ion.
List in g 9 - 1 3 . Loca t in g a W in dow s Spe cia l Folde r Private Declare Function SHGetFolderPath Lib "shell32" _ Alias "SHGetFolderPathA" _ (ByVal hwndOwner As Long, ByVal nFolder As Long, _ ByVal hToken As Long, ByVal dwFlags As Long, _ ByVal pszPath As String) As Long Private Declare Function GetTempPath Lib "kernel32" _ Alias "GetTempPathA" _ (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long 'More Commonly used CSIDL values. 'For the full list, search MSDN for "CSIDL Values" Private Const CSIDL_PROGRAMS As Long = &H2 Private Const CSIDL_PERSONAL As Long = &H5 Private Const CSIDL_FAVORITES As Long = &H6 Private Const CSIDL_STARTMENU As Long = &HB
Private Private Private Private Private Private Private Private Private Private Private
Const Const Const Const Const Const Const Const Const Const Const
CSIDL_MYDOCUMENTS As Long = &HC CSIDL_MYMUSIC As Long = &HD CSIDL_MYVIDEO As Long = &HE CSIDL_DESKTOPDIRECTORY As Long = &H10 CSIDL_APPDATA As Long = &H1A CSIDL_LOCAL_APPDATA As Long = &H1C CSIDL_INTERNET_CACHE As Long = &H20 CSIDL_WINDOWS As Long = &H24 CSIDL_SYSTEM As Long = &H25 CSIDL_PROGRAM_FILES As Long = &H26 CSIDL_MYPICTURES As Long = &H27
'Constants used in the SHGetFolderPath call Private Const CSIDL_FLAG_CREATE As Long = &H8000& Private Const SHGFP_TYPE_CURRENT = 0 Private Const SHGFP_TYPE_DEFAULT = 1 Private Const MAX_PATH = 260 'Public enumeration to give friendly names for the CSIDL values Public Enum SpecialFolderIDs sfAppDataRoaming = CSIDL_APPDATA sfAppDataNonRoaming = CSIDL_LOCAL_APPDATA sfStartMenu = CSIDL_STARTMENU sfStartMenuPrograms = CSIDL_PROGRAMS sfMyDocuments = CSIDL_PERSONAL sfMyMusic = CSIDL_MYMUSIC sfMyPictures = CSIDL_MYPICTURES sfMyVideo = CSIDL_MYVIDEO sfFavorites = CSIDL_FAVORITES sfDesktopDir = CSIDL_DESKTOPDIRECTORY sfInternetCache = CSIDL_INTERNET_CACHE sfWindows = CSIDL_WINDOWS sfWindowsSystem = CSIDL_SYSTEM sfProgramFiles = CSIDL_PROGRAM_FILES 'There is no CSIDL for the temp path, 'so we need to give it a dummy value 'and treat it differently in the function sfTemporary = &HFF End Enum 'Get the path for a Windows special folder Public Function SpecialFolderPath( _ ByVal uFolderID As SpecialFolderIDs) As String 'Create a buffer of the correct size Dim sBuffer As String * MAX_PATH Dim lResult As Long If uFolderID = sfTemporary Then 'Use GetTempPath for the temporary path lResult = GetTempPath(MAX_PATH, sBuffer)
'The GetTempPath call returns the length and a 'trailing \ which we remove for consistency SpecialFolderPath = Left$(sBuffer, lResult - 1) Else 'Call the function, passing the buffer lResult = SHGetFolderPath(0, _ uFolderID + CSIDL_FLAG_CREATE, 0, _ SHGFP_TYPE_CURRENT, sBuffer) 'The SHGetFolderPath function doesn't give us a 'length, so look for the first vbNullChar SpecialFolderPath = Left$(sBuffer, _ InStr(sBuffer, vbNullChar) - 1) End If End Function
The observant am ong you m ight have not iced t hat we've now com e across all t hree ways in which buffers are filled by API funct ions: Get UserNam e ret urns t he lengt h of t he t ext by m odifying t he input param et er. Get Tem pPat h ret urns t he lengt h of t he t ext as t he funct ion's ret urn value. SHGet FolderPat h doesn't ret urn t he lengt h at all, so we search for t he first vbNullChar.
Deleting a File to the Recycle Bin The VBA Kill st at em ent is used t o delet e a file, but does not send it t o t he recycle bin for pot ent ial recovery by t he user. To send a file t o t he recycle bin, we need t o use t he SHFileOperat ion funct ion, as shown in List ing 9- 14:
List in g 9 - 1 4 . D e le t in g a File t o t h e Re cycle Bin 'Structure to tell the SHFileOperation function what to do Private Type SHFILEOPSTRUCT hwnd As Long wFunc As Long pFrom As String pTo As String fFlags As Integer fAnyOperationsAborted As Boolean hNameMappings As Long lpszProgressTitle As String End Type
Private Declare Function SHFileOperation Lib "shell32.dll" _ Alias "SHFileOperationA" _ (ByRef lpFileOp As SHFILEOPSTRUCT) As Long Private Private Private Private
Const Const Const Const
FO_DELETE = &H3 FOF_SILENT = &H4 FOF_NOCONFIRMATION = &H10 FOF_ALLOWUNDO = &H40
'Delete a file, sending it to the recycle bin Sub DeleteToRecycleBin(ByVal sFile As String) Dim uFileOperation As SHFILEOPSTRUCT Dim lReturn As Long 'Fill the UDT with information about what to do With FileOperation .wFunc = FO_DELETE .pFrom = sFile .pTo = vbNullChar .fFlags = FOF_SILENT + FOF_NOCONFIRMATION + _ FOF_ALLOWUNDO End With 'Pass the UDT to the function lReturn = SHFileOperation(FileOperation) If lReturn 0 Then Err.Raise vbObjectError + 1, "Error deleting file." End If End Sub
There are t wo t hings t o not e about t his funct ion. First , t he funct ion uses a user- defined t ype t o t ell it what t o do, inst ead of t he m ore com m on m et hod of having m ult iple input param et ers. Second, t he funct ion ret urns a value of zero t o indicat e success. I f you recall t he Set CurDir funct ion in List ing 9- 12, it ret urns a value of zero t o indicat e failure! The only way t o know which t o expect is t o check t he Ret urn Values sect ion of t he funct ion's inform at ion page on MSDN.
Browsing for a Folder All versions of Excel have included t he Get OpenFilenam e and Get SaveAsFilenam e funct ions t o allow t he user t o select a filenam e t o open or save. Excel 2002 int roduced t he com m on Office FileDialog obj ect , which can be used t o browse for a folder, using t he code shown in List ing 9- 15, which result s in t he dialog shown in Figure 9- 3 .
List in g 9 - 1 5 . Usin g Ex ce l 2 0 0 2 's File D ia log t o Br ow se for a Folde r 'Browse for a folder, using the Excel 2002 FileDialog Sub BrowseForFolder() Dim fdBrowser As FileDialog 'Get the File Dialog object Set fdBrowser = Application.FileDialog(msoFileDialogFolderPicker) With fdBrowser 'Initialize it .Title = "Select Folder" .InitialFileName = "c:\" 'Display the dialog If .Show Then MsgBox "You selected " & .SelectedItems(1) End If End With End Sub
Figu r e 9 - 3 . Th e St a n da r d Office 2 0 0 2 Folde r Pick e r D ia log [View full size image]
We consider t his layout far t oo com plicat ed, when all we need is a sim ple t ree view of t he folders on t he com put er. We can use API funct ions t o show t he st andard Windows Browse for folder dialog shown in Figure 9- 4 , which our users t end t o find m uch easier t o use. The Windows dialog also gives us t he opt ion t o display som e descript ive t ext t o t ell our users what t hey should be select ing.
Figu r e 9 - 4 . Th e St a n da r d W in dow s Folde r Pick e r D ia log
Callbacks So far, every funct ion we've encount ered j ust does it s t hing and ret urns it s result . However, a range of API funct ions ( including t he SHBrowseForFolder funct ion t hat we're about t o use) int eract wit h t he calling program while t hey're working. This m echanism is known as a ca llba ck. Excel 2000 added a VBA funct ion called AddressOf, which provides t he address in m em ory where a given procedure can be found. This address is passed t o t he API funct ion, which calls back t o t he procedure found at t hat address as required. For exam ple, t he Enum Windows funct ion it erat es t hrough all t he t op- level windows, calling back t o t he procedure wit h t he det ails of each window it finds. Obviously, t he procedure being called m ust be defined exact ly as Windows expect s it t o be so t he API funct ion can pass it t he correct num ber and t ype of param et ers. The SHBrowseForFolder funct ion uses a callback t o t ell us when t he dialog is init ially shown, enabling us t o set it s capt ion and init ial select ion, and each t im e t he user select s a folder, enabling us t o check t he select ion and enable/ disable t he OK but t on. The full t ext for t he funct ion is cont ained in t he MBrowseForFolder m odule of t he API Exam ples.xls workbook and a slight ly sim plified version is shown in List ing 9- 16.
List in g 9 - 1 6 . Usin g Ca llba ck s t o I n t e r a ct w it h t h e W in dow s File Pick e r D ia log 'UDT to pass information to the SHBrowseForFolder function Private Type BROWSEINFO hOwner As Long pidlRoot As Long pszDisplayName As String lpszTitle As String ulFlags As Long lpfn As Long lParam As Long iImage As Long End Type 'Commonly used ulFlags constants 'Only return file system directories. 'If the user selects folders that are not 'part of the file system (such as 'My Computer'), 'the OK button is grayed. Private Const BIF_RETURNONLYFSDIRS As Long = &H1 'Use a newer dialog style, which gives a richer experience Private Const BIF_NEWDIALOGSTYLE As Long = &H40 'Hide the default 'Make New Folder' button Private Const BIF_NONEWFOLDERBUTTON As Long = &H200
'Messages sent from dialog to callback function Private Const BFFM_INITIALIZED = 1 Private Const BFFM_SELCHANGED = 2
'Messages sent to browser from callback function Private Const WM_USER = &H400 'Set the selected path Private Const BFFM_SETSELECTIONA = WM_USER + 102 'Enable/disable the OK button Private Const BFFM_ENABLEOK = WM_USER + 101
'The maximum allowed path Private Const MAX_PATH = 260 'Main Browse for directory function
Declare Function SHBrowseForFolder Lib "shell32.dll" _ Alias "SHBrowseForFolderA" _ (ByRef lpBrowseInfo As BROWSEINFO) As Long 'Gets a path from a pidl Declare Function SHGetPathFromIDList Lib "shell32.dll" _ Alias "SHGetPathFromIDListA" _ (ByVal pidl As Long, _ ByVal pszPath As String) As Long 'Used to set the browse dialog's title Declare Function SetWindowText Lib "user32" _ Alias "SetWindowTextA" _ (ByVal hwnd As Long, _ ByVal lpString As String) As Long 'A versions of SendMessage, to send strings to the browser Private Declare Function SendMessageString Lib "user32" _ Alias "SendMessageA" (ByVal hwnd As Long, _ ByVal wMsg As Long, ByVal wParam As Long, _ ByVal lParam As String) As Long 'Variables to hold the initial options, 'set in the callback function Dim msInitialPath As String Dim msTitleBarText As String 'The main function to initialize and show the dialog Function GetDirectory(Optional ByVal sInitDir As String, _ Optional ByVal sTitle As String, _ Optional ByVal sMessage As String, _ Optional ByVal hwndOwner As Long, _ Optional ByVal bAllowCreateFolder As Boolean) _ As String 'A variable to hold the UDT Dim uInfo As BROWSEINFO Dim sPath As String Dim lResult As Long 'Check that the initial directory exists On Error Resume Next sPath = Dir(sInitDir & "\*.*", vbNormal + vbDirectory) If Len(sPath) = 0 Or Err.Number 0 Then sInitDir = "" On Error GoTo 0 'Store the initials setting in module-level variables, 'for use in the callback function msInitialPath = sInitDir msTitleBarText = sTitle
'If no owner window given, use the Excel window 'N.B. Uses the ApphWnd function in MWindows If hwndOwner = 0 Then hwndOwner = ApphWnd 'Initialise the structure to pass to the API function With uInfo .hOwner = hwndOwner .pszDisplayName = String$(MAX_PATH, vbNullChar) .lpszTitle = sMessage .ulFlags = BIF_RETURNONLYFSDIRS + BIF_NEWDIALOGSTYLE _ + IIf(bAllowCreateFolder, 0, BIF_NONEWFOLDERBUTTON) 'Pass the address of the callback function in the UDT .lpfn = LongToLong(AddressOf BrowseCallBack) End With 'Display the dialog, returning the ID of the selection lResult = SHBrowseForFolder(uInfo) 'Get the path string from the ID GetDirectory = GetPathFromID(lResult) End Function
'Windows calls this function when the dialog events occur Private Function BrowseCallBack (ByVal hwnd As Long, _ ByVal Msg As Long, ByVal lParam As Long, _ ByVal pData As Long) As Long Dim sPath As String 'This is called by Windows, so don't allow any errors! On Error Resume Next Select Case Msg Case BFFM_INITIALIZED 'Dialog is being initialized, 'so set the initial parameters 'The dialog caption If msTitleBarText "" Then SetWindowText hwnd, msTitleBarText End If 'The initial path to display If msInitialPath "" Then SendMessageString hwnd, BFFM_SETSELECTIONA, 1, _ msInitialPath End If Case BFFM_SELCHANGED
'User selected a folder 'lParam contains the pidl of the folder, which can be 'converted to the path using GetPathFromID 'sPath = GetPathFromID(lParam) 'We could put extra checks in here, 'e.g. to check if the folder contains any workbooks, 'and send the BFFM_ENABLEOK message to enable/disable 'the OK button: 'SendMessage hwnd, BFFM_ENABLEOK, 0, True/False End Select End Function
'Converts a PIDL to a path string Private Function GetPathFromID(ByVal lID As Long) As String Dim lResult As Long Dim sPath As String * MAX_PATH lResult = SHGetPathFromIDList(lID, sPath) If lResult 0 Then GetPathFromID = Left$(sPath, InStr(sPath, Chr$(0)) - 1) End If End Function 'VBA doesn't let us assign the result of AddressOf 'to a variable, but does allow us to pass it to a function. 'This 'do nothing' function works around that problem Private Function LongToLong(ByVal lAddr As Long) As Long LongToLong = lAddr End Function
Let 's t ake a closer look at how t his all works. First , m ost of t he shell funct ions use t hings called PI DLs t o uniquely ident ify folders and files. For sim plicit y's sake, you can t hink of a PI DL as a handle t o a file or folder, and t here are API funct ions t o convert bet ween t he PI DL and t he norm al file or folder nam e. The Get Direct ory funct ion is t he m ain funct ion in t he m odule and is t he funct ion t hat should be called t o display t he dialog. I t st art s by validat ing t he ( opt ional) input param et ers, t hen populat es t he BROWSEI NFO user- defined t ype t hat is used t o pass all t he required inform at ion t o t he SHBrowseForFolder funct ion. The h Ow n e r elem ent of t he UDT is used t o provide t he parent window for t he dialog, which should be t he handle of t he m ain Excel window, or t he handle of t he userform window if showing t his dialog from a userform . The u lFla gs elem ent is used t o specify det ailed behavior for t he dialog, such as whet her t o show a Make Folder but t on. The full list of possible flags and t heir purpose can be found on MSDN by searching for t he SHBrowseForFolder funct ion. The lpfn elem ent is where we pass t he address of t he callback funct ion, BrowseCallBack.
We have t o wrap t he AddressOf value in a sim ple LongToLong funct ion, because VB doesn't let us assign t he value direct ly t o an elem ent of a UDT. Aft er t he UDT has been init ialized, we pass it t o t he SHBrowseForFolder API funct ion. That funct ion displays t he dialog and Windows calls back t o our BrowseCallBack funct ion, passing t he BFFM_I NI TI ALI ZED m essage. We respond t o t hat m essage by set t ing t he dialog's capt ion ( using t he Set WindowText API funct ion) and t he init ial folder select ion ( by sending t he BFFM_SETSELECTI ONA m essage back t o t he dialog wit h t he pat h st ring) . Every t im e t he user clicks a folder, it t riggers a Windows callback t o our BrowseCallBack funct ion, passing t he BFFM_SELCHANGED m essage and t he I D of t he select ed folder. All t he code t o respond t o t hat m essage is com m ent ed out in t his exam ple, but we could add code t o check whet her t he folder is a valid select ion for our applicat ion ( such as whet her it cont ains any workbooks) and enable/ disable t he OK but t on appropriat ely ( by sending t he BFFM_ENABLEOK m essage back t o t he dialog) . When t he user clicks t he OK or Cancel but t on, t he funct ion ret urns t he I D of t he select ed folder and execut ion cont inues back in t he Get Direct ory funct ion. We get t he t ext ual pat h from t he ret urned I D and ret urn it t o t he calling code.
Practical Examples All t he rout ines included in t his chapt er have been t aken out of act ual Excel applicat ions, so are t hem selves pract ical exam ples of API calls. The PETRAS applicat ion files for t his chapt er can be found on t he CD in t he folder \ Applicat ion\ Ch09Underst anding and Using Windows API Calls and now includes t he following files: Pe t r a sTe m pla t e .x lt The t im esheet t em plat e Pe t r a sAddin .x la The t im esheet dat a- ent ry support add- in Pe t r a sRe por t in g.x la The m ain report ing applicat ion Pe t r a sCon solida t ion .x lt A t em plat e t o use for new result s workbooks D e bu g.in i A dum m y file t hat t ells t he applicat ion t o run in debug m ode Pe t r a sI con .ico A new icon file, t o use for Excel's m ain window
PETRAS Timesheet Unt il t his chapt er, t he locat ion used by t he Post t o Net work rout ine has used Applicat ion.Get OpenFilenam e t o allow t he user t o select t he direct ory t o save t he t im esheet workbook t o. The problem wit h t hat call is t hat t he direct ory m ust already cont ain at least one file. I n t his chapt er, we add t he BrowseForFolder dialog and use t hat inst ead of Get OpenFilenam e, which allows em pt y folders t o be select ed. We've also added a new feat ure t o t he t im esheet add- in. I n previous versions you were prom pt ed t o specify t he consolidat ion locat ion t he first t im e you post ed a t im esheet workbook t o t he net work. When you select ed a locat ion, t hat locat ion was st ored in t he regist ry and from t here on out t he applicat ion sim ply read t he locat ion from t he regist ry whenever you post ed a new t im esheet . What t his didn't t ake int o account is t he possibilit y t hat t he consolidat ion locat ion m ight change. I f it did, you would have no way, short of edit ing t he applicat ion's regist ry ent ries direct ly, of swit ching t o t he new locat ion. Our new Specify Consolidat ion Folder feat ure enables you t o click a but t on on t he t oolbar and use t he Windows browse for folders dialog t o m odify t he consolidat ion folder. The SpecifyConsolidat ionFolder procedure is shown in List ing 9- 17 and t he updat ed t oolbar is shown in Figure 9- 5 .
List in g 9 - 1 7 . Th e N e w Spe cifyCon solida t ion Folde r Pr oce du r e Public Sub SpecifyConsolidationFolder()
Dim sSavePath As String InitGlobals ' Get the current consolidation path. sSavePath = GetSetting(gsREG_APP, gsREG_SECTION, _ gsREG_KEY, "") ' Display the browse for folders dialog with the initial ' path display set to the current consolidation folder. sSavePath = GetDirectory(sSavePath, _ gsCAPTION_SELECT_FOLDER, gsMSG_SELECT_FOLDER) If Len(sSavePath) > 0 Then ' Save the selected path to the registry. If Right$(sSavePath, 1) "\" Then _ sSavePath = sSavePath & "\" SaveSetting gsREG_APP, gsREG_SECTION, _ gsREG_KEY, sSavePath End If End Sub
Figu r e 9 - 5 . Th e Upda t e d PETRAS Tim e sh e e t Toolba r [View full size image]
Table 9- 2 sum m arizes t he changes t hat have been m ade t o t he t im esheet add- in for t his chapt er.
Ta ble 9 - 2 . Ch a n ge s t o t h e PETRAS Tim e sh e e t Add- in t o Use t h e Br ow se For Folde r Rou t in e M odu le
Pr oce du r e
MBrowseForFolder ( new m odule) MEnt r yPoint s
Ch a n g e I ncluded t he ent ire MBrowseForFolder m odule shown in List ing 9- 16
Post Tim eEnt r iesToNet w or k
Added call t o t he Get Direct ory funct ion in MBr ow seFor Folder
M odu le
Pr oce du r e
Ch a n g e
Specify Consolidat ionFolder
New feat ure t o updat e t he consolidat ion folder locat ion
PETRAS Reporting The changes m ade t o t he cent ral report ing applicat ion for t his chapt er are t o display a cust om icon for t he applicat ion and t o enable t he user t o close all t he result s workbooks sim ult aneously, by holding down t he Shift key while clicking t he File > Close m enu. The det ailed changes are shown in Table 9- 3, and List ing 9- 18 shows t he new MenuFileClose rout ine t hat includes t he check for t he Shift key.
List in g 9 - 1 8 . Th e N e w M e n u File Close Rou t in e , Ch e ck in g for a Sh ift + Close 'Handle the File > Close menu Sub MenuFileClose() Dim wkbWorkbook As Workbook 'Ch09+ 'Check for a Shift+Close If IsKeyPressed(gksKeyboardShift) Then 'Close all results workbooks For Each wkbWorkbook In Workbooks If IsResultsWorkbook(wkbWorkbook) Then CloseWorkbook wkbWorkbook End If Next Else 'Ch09'Close only the active workbook If IsResultsWorkbook(ActiveWorkbook) Then CloseWorkbook ActiveWorkbook End If End If End Sub
Ta ble 9 - 3 . Ch a n ge s t o t h e PETRAS Re por t in g Applica t ion for Ch a pt e r 9
M odu le
Pr oce du r e
Ch a n g e
MAPI Wr apper s ( new m odule)
ApphWnd
I ncluded List ing 9- 4 t o obt ain t he handle of Excel's m ain window
MAPI Wr apper s ( new m odule)
Set I con
I ncluded List ing 9- 7 t o display a cust om icon, read from t he new Pet rasI con.ico file.
MAPI Wr apper s
I sKeyPr essed
I ncluded List ing 9- 8 t o check for t he Shift key held down when clicking File > Close
MGlobals
Added a const ant for t he icon filenam e
MWor k space
Configur eExcelEnvir onm ent
Added a call t o Set I con
MEnt r yPoint s
MenuFileClose
Added check for Shift key being held down, shown in List ing 9- 17, doing a Close All if so
Lat er chapt ers, part icularly Chapt er 10 Userform Design and Best Pract ices, use m ore of t he rout ines and concept s int roduced in t his chapt er.
Conclusion The Excel obj ect m odel provides an ext rem ely rich set of t ools for us t o use when creat ing our applicat ions. By including calls t o Windows API funct ions, we can enhance our applicat ions t o give t hem a t ruly professional look and feel. This chapt er has explained m ost of t he uses of API funct ions t hat are com m only encount ered in Excel applicat ion developm ent . All t he fundam ent al concept s have been explained and you should now be able t o int erpret and underst and new uses of API funct ions as you encount er t hem . All of t he exam ple rout ines included in t his chapt er have been t aken from act ual Excel applicat ions and are ready for you t o use in your own workbooks.
Chapter 10. Userform Design and Best Practices Userform s are a fundam ent al part of m ost applicat ions' user int erfaces, ranging from cust om m essage and input boxes t o very com plex dat a- ent ry form s. This chapt er explains how t o get t he m ost out of Excel's userform s.
Principles When we design and code userform s, we st rive t o adhere t o a sm all set of basic principles whenever possible, explained below. We have found t hat following t hese principles has result ed in userform s t hat are easy t o use, easy t o code and easy t o m aint ain. Alt hough som e of t hese principles m ay seem a lit t le art ificial at first , experience has shown t hat st icking t o t hem gives longt erm rewards.
Keep It Simple A userform should not need a help file t o explain how t o fill it in. When present ed wit h a userform , our users should int uit ively be able t o use it . I n pract ice, t his m eans having a relat ively sm all num ber of cont rols on a userform t hat are well posit ioned, clearly labeled and in appropriat e groupings and orders t o m at ch t he t ask for which t he userform will be used. When designing a userform t o help wit h a com plex t ask, a wizard st yle should be used, breaking t he userform down int o m ult iple st eps, each of which adheres t o t he Keep I t Sim ple, St upid ( KI SS) principle. There are, of course, sit uat ions t hat require com plex userform s. I n t hese cases, ext ra effort should be invest ed t o m ake t he userform as sim ple t o use as possible. Making a com plex userform as easy as possible for t he user usually requires t he m ost effort on t he part of t he program m er and oft en result s in quit e com plex code!
Display Canvas, Not Business Rules A userform is a user int erface elem ent , not a place t o im plem ent business logic. The user int eract ion should always be separat ed from t he business response t o t hat int eract ion, at least logically if not physically. I n pract ice, t his m eans t hat t he only code t hat should be included in cont rols' event procedures is eit her ( a) changing anot her cont rol's propert ies, or ( b) calling funct ions in t he business logic layer. Conversely, t he code in t he business logic layer should never refer direct ly t o cont rols on t he userform and ideally should not assum e t hat a specific display m echanism is being used ( such as a set of opt ion but t ons vs. a list box) . So what is business logic in t his cont ext ? Figure 10- 1 shows a sim ple userform wit h a com bo box t o select a region and a t wo- colum n list t o show sales by product .
Figu r e 1 0 - 1 . A Sim ple Use r for m
I f t he code in t he com bobox_change event procedure ident ifies t he region, ret rieves t he product s for t he region, ret rieves t he t ot al sales for each product and adds t hem t o t he list box, it 's im plem ent ing business logic and is doing t oo m uch. When t he user select s a new region, t here are t wo t hings we need t o specify: ( 1) t he appropriat e response, and ( 2) t he dat a required t o sat isfy t hat response. I n our exam ple, t he appropriat e response is t o populat e t he list of product s and t he dat a required t o sat isfy t he response is t he list of product s and t he t ot al sales for each. At a m inim um , t he dat a required t o sat isfy t he response should be obt ained from t he business logic layer. I n t his case, we would have a funct ion in t he business logic layer t hat t akes a region as a param et er and ret urns an array of product s and t ot al sales. I t does t his by ret rieving t he underlying dat a from t he dat a access layer and populat ing t he array. Code in t he com bobox_change event would read t he select ed region from t he com bo box, call t he business logic layer funct ion t o get t he array of product s and sales and writ e t hat array t o t he list box. List ing 10- 1 shows an exam ple of t his m echanism .
List in g 1 0 - 1 . Th e Use r I n t e r fa ce La ye r D e t e r m in e s t h e Re spon se '*********************************** '* User Interface Layer, FSimpleForm '*********************************** 'Handle selecting a different region Private Sub cboRegion_Change() Dim vaProductSales As Variant 'Get the Product/Sales array for the selected region 'from the business logic layer
vaProductSales = GetProductSalesForRegion(cboRegion.Value) 'Populate the list box lstProducts.List = vaProductSales End Sub
At t he ext rem e, we int roduce a new user int erface support ( UI S) layer t hat cont ains t he code t o det erm ine t he appropriat e response for each user act ion. The event procedure would t hen cont ain a single line t hat calls a procedure in t he UI S layer, passing t he select ed region. The UI S layer calls t he funct ion in t he business logic layer t o ret rieve t he array of product s and t ot al sales, t hen t ells t he userform t o populat e t he list wit h t he array. This m echanism t reat s t he userform as not hing m ore t han a drawing and int eract ion layer and is an ext rem ely useful way t o handle com plex form s. An exam ple of t his t echnique can be found in t he UI SLayer.xls workbook on t he CD. I n List ing 10- 2, t he UI S layer is physically locat ed in a separat e class m odule t hat t ells t he userform what t o do by raising cust om event s. For m ore det ails about class m odules and cust om event s, see Chapt er 7Using Class Modules t o Creat e Obj ect s.
List in g 1 0 - 2 . Th e Use r I n t e r fa ce Su ppor t La ye r D e t e r m in e s t h e Re spon se '************************************* '* User Interface Layer in '* userform FComplexForm '************************************* 'UIS Event handler Dim WithEvents mclsUISComplexForm As CUISComplexForm
'Initialize our UIS class Private Sub UserForm_Initialize() Set mclsUISComplexForm = New CUISComplexForm End Sub ' ' Control events, to handle the user telling us ' to do something. In most cases, we just pass it ' on to the UIS class. ' 'Handle selecting a different region Private Sub cboRegion_Change() 'Tell the UIS layer that the user 'just selected a different region mclsUISComplexForm.RegionSelected cboRegion.Value
End Sub
' ' UIS class events, to handle the UIS layer ' telling us to do something ' 'Populate the Product Sales List Private Sub mclsUISComplex_PopulateProductList( _ vaProductSales As Variant) lstProducts.List = vaProductSales End Sub
'************************************************ '* User Interface Support Layer '* in class CUISComplexForm '************************************************ 'Events to tell the userform what to do Public Event PopulateProductList(vaProductSales As Variant)
'The user selected a different region. Public Sub RegionSelected(ByVal sRegion As String) Dim vaProductSales As Variant 'Get the Product/Sales array from the business logic layer vaProductSales = GetProductSalesForRegion(sRegion) 'Tell the userform to populate the products list RaiseEvent PopulateProductList(vaProductSales) End Sub
There is obviously m ore overhead in using an int erm ediat e UI S layer, but it reduces m ost of t he userform event procedures t o one- line calls int o t he UI S layer, enabling us t o concent rat e on t he det ail of t he user experience wit hin t he userform m odule. This m akes t he userform it self m uch easier t o m aint ain. Not ice also t hat t he UI S class has no knowledge of how t he inform at ion is obt ained or displayedall it knows is t hat when it 's given a region, it should t ell t he userform t hat t here's a new list of product s. The UI S class could be used by m ult iple versions of t he sam e userform perhaps wit h each one opt im ized for different screen resolut ions.
Use Classes, Not the Default Instance
Whenever we add a userform t o a proj ect , we aut om at ically get a default inst ance of t he userform creat ed for us. This is effect ively a global variable t hat has t he sam e nam e as t he userform 's class nam e, declared As New. This m eans t hat as soon as we refer t o t he userform , Excel creat es t he default inst ance for us ( t his is known as a u t o- in st a n t ia t ion ) . When we unload t he userform , t he default inst ance is dest royed and when we refer t o it again, t he default inst ance is re- creat ed. Consider a userform , FMyForm , cont aining a single t ext box, t xt Nam e. The code in List ing 10- 3 shows t he userform and displays t he nam e.
List in g 1 0 - 3 . Usin g t h e Use r for m 's D e fa u lt I n st a n ce Sub TestDefaultInstance() 'Show the userform FMyForm.Show 'Show the contents of the text box MsgBox "The name is: " & FMyForm.txtName.Text End Sub
Run t he procedure, t ype a nam e int o t he t ext box and close t he userform using t he [ x] in t he t opright corner. The procedure runs wit hout any errors, but t he m essage box doesn't show us t he nam e we t yped in! This is because when t he [ x] was clicked, t he userform was unloaded and anyt hing ent ered int o it was lost . Wit hin t he MsgBox line, t he reference t o FMyForm caused Excel t o creat e a new inst ance of t he userform , in which t he nam e is blank. D o n ot u se de fa u lt in st a n ce s. Userform s are j ust a special t ype of class m odule and t hey should be t reat ed like class m odules. By doing so, we gain cont rol over when t he userform is creat ed and dest royed, prevent ing t he t ype of bug dem onst rat ed in List ing 10- 3. List ing 10- 4 shows t he sam e rout ine, t reat ing t he userform as a class. This t im e, t he nam e is displayed correct ly.
List in g 1 0 - 4 . Usin g t h e Use r for m Lik e a Cla ss Sub TestClassInstance() 'Define our object variable Dim frmMyForm As FMyForm 'Set our object variable to be a new instance of the userform Set frmMyForm = New FMyForm 'Show the userform frmMyForm.Show 'Show the contents of the text box
MsgBox "The name is: " & frmMyForm.txtName.Text 'If showing the userform modeless, we have to unload it Unload frmMyForm End Sub
Unfort unat ely, using a userform like t his gives us a m inor problem t hat we need t o be aware of and workaround: I f t he userform is unloaded while our obj ect variable is referring t o it , we will oft en get an aut om at ion error. This is easily avoided by ensuring our userform is only ever hidden inst ead of being unloaded. The code in List ing 10- 5 can be added t o any userform s t hat have t he st andard OK and Cancel but t ons.
List in g 1 0 - 5 . H idin g I n st e a d of Un loa din g a For m 'Store whether the user OK'd or Cancel'd Dim mbOK As Boolean 'Handle the OK button Private Sub cmdOK_Click() mbOK = True Me.Hide End Sub 'Handle the Cancel button Private Sub cmdCancel_Click() mbOK = False Me.Hide End Sub 'Make the [x] behave the same as Cancel Private Sub UserForm_QueryClose(Cancel As Integer, _ CloseMode As Integer) If CloseMode = vbFormControlMenu Then cmdCancel_Click Cancel = True End If End Sub 'Return whether the OK or Cancel button was clicked Public Property Get OK() As Boolean OK = mbOK End Property
Expose Properties and Methods, Not Controls Following t he philosophy of t reat ing a userform like a class, we should only int eract wit h t he userform via propert ies and m et hods t hat we add t o t he userform 's class m odule; we should never refer t o individual cont rols from out side t he userform 's m odule, nor should we set any propert ies of t he userform it self. Proper encapsulat ion dict at es t hat everyt hing t o do wit h a userform should be cont ained wit hin t he userform . By adding propert ies and m et hods t o isolat e a userform 's cont rols from ext ernal code, we gain t he abilit y t o renam e or change t he st yle of any of t he cont rols, knowing t hat we won't be breaking any code t hat uses t he userform . I m agine a userform wit h a set of t hree opt ion but t ons t o select a level of det ail for a report . I f we were t o allow ext ernal code t o direct ly access t he cont rols, we m ight be t em pt ed t o writ e code like List ing 10- 6 ( where we've assum ed t he form includes t he code from List ing 10- 5) .
List in g 1 0 - 6 . Usin g a Use r for m 's Con t r ols D ir e ct ly Sub UseTheControls() Dim frmOptions As FOptions Set frmOptions = New FOptions 'Show the userform frmOptions.Show If frmOptions.OK Then 'Which option was selected? If frmOptions.optDetailed.Value Then RunDetailedReport ElseIf frmOptions.optNormal.Value Then RunNormalReport ElseIf frmOptions.optSummary.Value Then RunSummaryReport End If End If End Sub
The result of doing t his is t hat t he calling code is very t ight ly bound t o t he physical layout of t he userform , so if we want t o change t he userform 's layout such as t o use a com bo box inst ead of t he t hree opt ion but t onswe have t o check wherever t he userform is used and change t hat code as well as t he code wit hin t he userform 's m odule. I nst ead, we should expose everyt hing using propert y procedures, so t he calling code does not need t o know how t he propert y is physically represent ed on t he userform . List ing 10- 7 adds a Det ailLevel propert y t o t he userform , ret urning t he level of det ail as an enum erat ion, which t he
calling code uses t o decide which report t o run.
List in g 1 0 - 7 . Usin g Pr ope r t y Pr oce du r e s ' 'Within the userform FOptions ' 'Enum for the levels of detail Public Enum odlOptionDetailLevel odlDetailLevelDetailed odlDetailLevelNormal odlDetailLevelSummary End Enum 'Property to return the level of detail Public Property Get DetailLevel() As odlOptionDetailLevel 'Which option was selected? If optDetailed.Value Then DetailLevel = odlDetailLevelDetailed ElseIf optNormal.Value Then DetailLevel = odlDetailLevelNormal ElseIf optSummary.Value Then DetailLevel = odlDetailLevelSummary End If End Property ' 'The calling code ' Sub UseAProperty() Dim frmOptions As FOptions Set frmOptions = New FOptions 'Show the userform frmOptions.Show If frmOptions.OK Then 'Which option was selected? If frmOptions.DetailLevel = odlDetailLevelDetailed Then RunDetailedReport ElseIf frmOptions.DetailLevel = odlDetailLevelNormal Then RunNormalReport
ElseIf frmOptions.DetailLevel = odlDetailLevelSummary Then RunSummaryReport End If End if End Sub
Now if we want t o change t he opt ion but t ons t o a com bo box, all of t he changes are cont ained wit hin t he userform and it s code m odule, m aking m aint enance m uch easier and m uch less prone t o int roducing new bugs. Unfort unat ely, all t he cont rols on a userform and all t he userform 's propert ies are always exposed t o ext ernal code, so any propert ies and m et hods we add get lost in t he I nt elliSense list . I n Chapt er 11 I nt erfaces, we explain how t o define and use our own int erfaces, which allow us t o expose only t he propert ies and m et hods t hat we want t o be called.
Control Fundamentals There are a few fundam ent al det ails t hat we sim ply have t o get right when working wit h cont rols on user for m s.
Naming As we discussed in Chapt er 3 Excel and VBA Developm ent Best Pract ices, all our cont rols should be given m eaningful nam es and include a t wo- or t hree- charact er prefix t o ident ify t he cont rol t ype. This enables us t o easily ident ify t he cont rol in code and when set t ing t he t ab order. For exam ple, we have no idea which but t on Com m andBut t on1 is, but we can easily ident ify cm dOK and so on.
Layering I f we include t he background, userform s have t hree drawing layers. When we add a cont rol t o a userform , it get s added t o one of t he t op t wo layers, depending on t he t ype of cont rol. The t hree layers are ident ified as follows:
1 . The userform background and it s scrollbar 2 . The Label, CheckBox, Com boBox, Com m andBut t on, I m age, Opt ionBut t on, RefEdit , ScrollBar, SpinBut t on, TabSt rip, ToggleBut t on and Text Box cont rols 3 . The Fram e, List Box, Mult iPage and ot her Act iveX cont rols Cont rols in layer 2 can overlap each ot her, but will always be drawn behind cont rols in layer 3, whereas all t he cont rols in layer 3 can overlap each ot her. Fort unat ely, layer 3 includes t he Fram e cont rol, so if we want t o draw any of t he ot her cont rols on t op of a layer 3 cont rol, we can put it inside a fram e. Wit hin a layer, we can arrange our cont rols' z- order using t he Form at > Order m enu it em s.
Positioning All cont rols on a userform should be aligned bot h horizont ally and vert ically wit h a consist ent am ount of space bet ween t hem . When people read form s, t hey read t he t ext of each cont rol, so it is t he t ext t hat we should align and not t he edges of t he cont rols. I f we have Snap t o Grid swit ched on and add a t ext box and a label t o a userform , t he label will need t o be m oved down ( usually by four pixels) t o ensure t he t ext of t he label aligns wit h t he t ext ent ered int o t he t ext box. This can be done by edit ing t he label's Top propert y, t o add four pixels, but how big is a pixel? I n Chapt er 9 Underst anding and Using Windows API Calls, we explained t hat t he pixel size depends on t he user's
choice of dot s per inch, but is usually 0.75 point s for t he Norm al set t ing of 96 DPI and 0.6 point s for t he Large set t ing of 120 DPI . So t o m ove a cont rol by one pixel, we have t o add 0.75 or 0.6. Moving and sizing cont rols pixel by pixel is m ade m uch easier by using t he VBE Tools Cont rol Nudger t oolbar, shown in Figure 10- 2. This t oolbar is part of t he VBE Tools add- in, included on t he CD in t he \ Tools\ folder .
Figu r e 1 0 - 2 . Th e VBE Tools Con t r ol N u dge r Toolba r
Tab Orders and Accelerator Keys As designers of userform s, we t end t o be " m ousers" and oft en forget t hat m any people prefer t o use t he keyboard t o navigat e around userform s. We m ust rem em ber t o facilit at e keyboard usage by ensuring we give our cont rols t he correct t ab order and/ or accelerat or keys. The t ab order should m at ch t he nat ural reading orderleft t o right and t op t o bot t om and should include labels as well as cont rols. I f t he userform includes som e cont ainer cont rols, such as t he Fram e and Mult iPage cont rols, we m ust rem em ber t o set t he t ab order for t he cont rols t hey cont ain as well as for t hose direct ly on t he userform . We do t his by ensuring t he cont ainer cont rol is select ed before clicking View > Tab Order. Accelerat or keys enable us t o use t he Alt + key com binat ion t o j um p direct ly t o t he cont rol. The accelerat or key for a cont rol is ident ified by an underscore under t he appropriat e let t er of t he cont rol's capt ion. I f we set t he accelerat or key for a label and use t hat accelerat or key, t he focus will j um p t o t he next cont rol in t he userform 's t ab order. This behavior allows us t o provide keyboard access t o cont rols t hat do not have a capt ion, such as list boxes, edit boxes and so fort h.
Data Binding Many userform cont rols have propert ies t hat allow t hem t o be bound t o worksheet cells t o specify t heir cont ent s and/ or ret urn t heir value/ t ext . Don't use t hem . They are t here for beginners t o creat e sim ple, quick- and- dirt y form s, but t hey very quickly becom e m ore t rouble t han t hey're wort h. Using VBA t o set t he cont rols' cont ent s and handle t he dat a ent ry gives us m uch m ore flexibilit y and enables us t o validat e t he dat a before updat ing cells.
Event Handling
The MSForm s cont rols used on userform s have a fairly rich event m odel. Deciding which event t o use for a given purpose can be quit e daunt ing. Our recom m endat ion is t o follow t he principle of keeping it sim ple and use t he basic Change or Click event s for m ost sit uat ions. I n part icular, don't t ry t o int ercept t he KeyDown or KeyPress event s in an at t em pt t o force num eric ent ry int o a cont rol. I f your code prevent s let t ers, it also prevent s t he valid use of exponent ial not at ion, such as 1E3 for 1000. I f you t ry t o prevent m ult iple decim al separat ors, you have t o m ake sure you're allowing t he decim al separat or set in t he Regional Set t ings applet ; and if t he user put t he decim al in t he wrong place, you're forcing t hem t o delet e t he wrong one before t yping t he new one. I t is m uch bet t er ( and easier for us) t o allow t he user t o t ype in what ever he chooses, t hen validat e t he ent ry using VBA's I sNum eric( ) funct ion. Cont rol event s are fired bot h by user act ion and when t he cont rol is changed in code. We can use Applicat ion.EnableEvent s t o t urn event s on and off for Excel's obj ect s, but t hat has no effect on t he MSForm s obj ect m odel. We can get t he sam e level of cont rol over when event s are handled by using a m odule- level variable t hat is checked at t he st art of all our event procedures, as shown in List ing 10- 8.
List in g 1 0 - 8 . H a n dlin g Con t r ols' Eve n t s 'Module-level variable to control events firing Dim mbStopEvents As Boolean
'Handle clicking a 'Get Data' button Private Sub btnGetNames_Click() Dim vaNames As Variant 'Get a list of names from somewhere vaNames = Array("Rob", "Stephen", "John", "Robert") 'Turn off events while populating the controls mbStopEvents = True 'Populate controls. 'The Clear method triggers the Change event. lstNames.Clear lstNames.List = vaNames 'Turn events on again mbStopEvents = False 'Select the first name, allowing the Change event to fire If lstNames.ListCount > 0 Then lstNames.ListIndex = 0 End If End Sub
'Handle selecting a name Private Sub lstNames_Change() 'Don't do anything if we've stopped events If mbStopEvents Then Exit Sub 'Process selecting a name from the list MsgBox "You selected " & lstNames.Text End Sub
Validation Most userform s have cont rols for dat a ent ry and a pair of OK and Cancel but t ons. When t he OK but t on is clicked, t he dat a is writ t en t o t he sheet / dat abase/ obj ect m odel. When t he Cancel but t on is clicked, t he dat a is ignored. At som e point bet ween t he user ent ering dat a and t hat dat a being st ored, it m ust be validat ed. Many people are t em pt ed t o use t he BeforeUpdat e event for t heir validat ion code, because it has a Cancel propert y t hat can be used t o force t he user t o ent er valid dat a. Don't use it . Our userform s should never get in t he way of users, or int errupt t heir work, yet should also provide feedback as soon as possible, t o give t hem t he opport unit y ( but not force t hem ) t o correct t heir m ist akes. Our recom m endat ion is t o use an unobt rusive form of validat ion in each cont rol's Aft erUpdat e event and an int rusive form of validat ion in t he OK but t on's Click event . By int rusive, we m ean som et hing t hat st ops t he user from cont inuing, such as displaying a m essage box. By unobt rusive, we m ean som et hing t hat alert s t he user t o an error sit uat ion, but allows t hem t o cont inue, such as t urning t he background red and set t ing t he t oolt ip t o show t he error m essage. List ing 10- 9 shows t he code t o validat e a sim ple userform t hat cont ains t wo sales figures, and Figure 10- 3 shows t he userform wit h an error in t he first edit box.
List in g 1 0 - 9 . Va lida t in g Con t r ols Option Explicit 'When exiting the controls, we perform some 'nonintrusve validation, by calling the 'CheckNumeric function Private Sub txtSalesNorth_AfterUpdate() CheckNumeric txtSalesNorth End Sub Private Sub txtSalesSouth_AfterUpdate() CheckNumeric txtSalesSouth End Sub
'In the OK button, we use the same CheckNumeric
'function to show some intrusive validation 'messages. Private Sub btnOK_Click() Dim Dim Dim Dim Dim
dNorth As Double dSouth As Double sError As String sAllErrors As String bFocusSet As Boolean
'Validate the North Sales text box, 'returning the value or some error text If Not CheckNumeric(txtSalesNorth, dNorth, sError) Then 'Set the focus to the first control with an error If Not bFocusSet Then txtSalesNorth.SetFocus bFocusSet = True End If 'Build an error string, so we display all errors on the 'userform in one error message sAllErrors = sAllErrors & "North Sales:" & sError & vbLf End If 'Validate the South Sales text box, 'returning the value or some error text If Not CheckNumeric(txtSalesSouth, dSouth, sError) Then 'Set the focus to the first control with an error If Not bFocusSet Then txtSalesSouth.SetFocus bFocusSet = True End If 'Build an error string, so we display all errors on the 'userform in one error message sAllErrors = sAllErrors & "South Sales:" & sError & vbLf End If 'Display any errors we got If Len(sAllErrors) > 0 Then MsgBox "Please correct the following error(s):" & _ vbLf & sAllErrors, vbOKOnly Else 'No errors, so store the result ActiveSheet.Range("rngNorthSales").Value = dNorth ActiveSheet.Range("rngSouthSales").Value = dSouth 'And unload the userform Unload Me End If
End Sub
'The Cancel button just unloads the userform. 'This assumes the form is self-contained, so the 'calling routine doesn't need to know if the user 'OK'd or Cancelled the form. Private Sub btnCancel_Click() Unload Me End Sub
'Function to check a control (textbox or combobox) for 'numeric entry ' 'Parameters: txtData [in] The textbox or combobox ' dResult [out] The numeric value from the box ' sError [out] The text of the error message ' Function CheckNumeric(ByRef txtData As MSForms.Control, _ Optional ByRef dResult As Double, _ Optional ByRef sError As String) As Boolean Const sERR As String = ". Error: " Dim lErrPos As Long 'Remove any existing tooltip error text lErrPos = InStr(1, txtData.ControlTipText, sERR) If lErrPos > 0 Then txtData.ControlTipText = Left$(txtData.ControlTipText, _ lErrPos - 1) End If 'Check for valid entry If txtData.Text = "" Then 'Allow empty dResult = 0 sError = "" CheckNumeric = True 'And give the text box its usual background txtData.BackColor = vbWindowBackground ElseIf IsNumeric(txtData.Text) Then 'Numeric, so set the return values dResult = CDbl(txtData.Text) sError = "" CheckNumeric = True 'And give the text box its usual background txtData.BackColor = vbWindowBackground
Else 'Not numeric, so set the return values dResult = 0 sError = "Entry is not a number." CheckNumeric = False 'Give the text box a red background txtData.BackColor = vbRed 'And add the error message to the tooltip txtData.ControlTipText = txtData.ControlTipText & _ sERR & sError End If End Function
Figu r e 1 0 - 3 . An Un obt r u sive Er r or I n dica t or
Visual Effects Userform Window Styles We m ent ioned briefly in Chapt er 9 Underst anding and Using Windows API Calls t hat we can use a few API funct ions t o m odify t he appearance of a window's border and/ or t it le bar. List ing 10- 10 shows t he Set Userform Appearance procedure t o do j ust t hat for userform s, enabling us t o independent ly set t he following at t ribut es: Whet her t he userform has a t it le bar Whet her t he t it le bar is t he norm al size or t he sm all size used for float ing t oolbars Whet her t he userform is resizable Whet her t he userform has a m axim ize but t on Whet her t he userform has a m inim ize but t on Whet her t he userform has a close but t on Whet her t he userform has an icon and t he icon t o use To set t he appearance for a userform we call t he Set Userform Appearance procedure from t he Userform _I nit ialize event , passing in t he required set of values from t he Userform WindowSt yles enum erat ion, added t oget her. This code is included on t he CD in t he MForm St yles m odule in t he Userform St yles.xls workbook and uses t he FindOurWindow and Set I con procedures shown in Chapt er 9 Underst anding and Using Windows API Calls.
List in g 1 0 - 1 0 . M odifyin g a Use r for m 's W in dow St yle s 'Windows API calls to do all the dirty work Private Declare Function GetWindowLong Lib "user32" Alias _ "GetWindowLongA" (ByVal hWnd As Long, _ ByVal nIndex As Long) As Long Private Declare Function SetWindowLong Lib "user32" Alias _ "SetWindowLongA" (ByVal hWnd As Long, _ ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function GetSystemMenu Lib "user32" _ (ByVal hWnd As Long, ByVal bRevert As Long) As Long
Private Declare Function DeleteMenu Lib "user32" _ (ByVal hMenu As Long, ByVal nPosition As Long, _ ByVal wFlags As Long) As Long Private Declare Function DrawMenuBar Lib "user32" _ (ByVal hWnd As Long) As Long 'Window Private Private Private Private Private Private Private Private Private Private
API constants Const GWL_STYLE As Long = (-16) Const GWL_EXSTYLE As Long = (-20) Const WS_CAPTION As Long = &HC00000 Const WS_SYSMENU As Long = &H80000 Const WS_THICKFRAME As Long = &H40000 Const WS_MINIMIZEBOX As Long = &H20000 Const WS_MAXIMIZEBOX As Long = &H10000 Const WS_EX_DLGMODALFRAME As Long = &H1 Const WS_EX_TOOLWINDOW As Long = &H80 Const SC_CLOSE As Long = &HF060
'Public enum of our userform styles Public Enum UserformWindowStyles uwsNoTitleBar = 0 uwsHasTitleBar = 1 uwsHasSmallTitleBar = 2 uwsHasMaxButton = 4 uwsHasMinButton = 8 uwsHasCloseButton = 16 uwsHasIcon = 32 uwsCanResize = 64 uwsDefault = uwsHasTitleBar Or uwsHasCloseButton End Enum
'Routine to set a userform's window style, 'called from Userform_Initialize event Sub SetUserformAppearance(ByRef frmForm As Object, _ ByVal lStyles As UserformWindowStyles, _ Optional ByVal sIconPath As String) Dim Dim Dim Dim
sCaption As String hWnd As Long lStyle As Long hMenu As Long
'Find the window handle of the form sCaption = frmForm.Caption frmForm.Caption = "FindThis" & Rnd hWnd = FindOurWindow("ThunderDFrame", frmForm.Caption) frmForm.Caption = sCaption 'If we want a small title bar, we can't have an icon, 'max or min buttons as well
If lStyles And uwsHasSmallTitleBar Then lStyles = lStyles And Not (uwsHasMaxButton Or _ uwsHasMinButton Or uwsHasIcon) End If 'Get the normal windows style bits lStyle = GetWindowLong(hWnd, GWL_STYLE) 'Update the normal style bits appropriately 'If we want and icon or Max, Min or Close buttons, 'we have to have a system menu ModifyStyles lStyle, lStyles, uwsHasIcon Or _ uwsHasMaxButton Or uwsHasMinButton Or _ uwsHasCloseButton, WS_SYSMENU 'Most things need a title bar! ModifyStyles lStyle, lStyles, uwsHasIcon Or _ uwsHasMaxButton Or uwsHasMinButton Or _ uwsHasCloseButton Or uwsHasTitleBar Or _ uwsHasSmallTitleBar, WS_CAPTION ModifyStyles lStyle, lStyles, uwsHasMaxButton, WS_MAXIMIZEBOX ModifyStyles lStyle, lStyles, uwsHasMinButton, WS_MINIMIZEBOX ModifyStyles lStyle, lStyles, uwsCanResize, WS_THICKFRAME 'Update the window with the normal style bits SetWindowLong hWnd, GWL_STYLE, lStyle 'Get the extended style bits lStyle = GetWindowLong(hWnd, GWL_EXSTYLE) 'Modify them appropriately ModifyStyles lStyle, lStyles, uwsHasSmallTitleBar, _ WS_EX_TOOLWINDOW 'The icon is different to the rest-'we set a bit to turn it off, not on! If lStyles And uwsHasIcon Then lStyle = lStyle And Not WS_EX_DLGMODALFRAME 'Set the icon, if given If Len(sIconPath) > 0 Then SetIcon hWnd, sIconPath End If Else lStyle = lStyle Or WS_EX_DLGMODALFRAME End If 'Update the window with the extended style bits SetWindowLong hWnd, GWL_EXSTYLE, lStyle
'The Close button is handled by removing it from the 'control menu, not through a window style bit If lStyles And uwsHasCloseButton Then 'We want it, so reset the control menu hMenu = GetSystemMenu(hWnd, 1) Else 'We don't want it, so delete it from the control menu hMenu = GetSystemMenu(hWnd, 0) DeleteMenu hMenu, SC_CLOSE, 0& End If 'Refresh the window with the changes DrawMenuBar hWnd End Sub
'Helper routine to check if one of our style bits is set 'and set/clear the corresponding Windows style bit Private Sub ModifyStyles(ByRef lFormStyle As Long, _ ByVal lStyleSet As Long, _ ByVal lChoice As UserformWindowStyles, _ ByVal lWS_Style As Long) If lStyleSet And lChoice Then lFormStyle = lFormStyle Or lWS_Style Else lFormStyle = lFormStyle And Not lWS_Style End If End Sub
Disabling the Close Button Even t hough t he procedure shown in List ing 10- 10 can be used t o rem ove t he close m enu from a userform , t he st andard Windows keyst roke of Alt + F4 t o close a window can st ill be used t o close t he form . We handle t his by hooking t he UserForm _QueryClose event , as shown in List ing 10- 11. The QueryClose event can be used wit hout rem oving t he Close but t on, but t hat gives conflict ing m essages t o t he user; you're showing an enabled Close but t on, but it doesn't do anyt hing.
List in g 1 0 - 1 1 . Pr e ve n t in g t h e Use r Closin g t h e Use r for m 'Set the form to have a (small) title bar, but no Close button Private Sub UserForm_Initialize() SetUserformAppearance Me, uwsHasSmallTitleBar
End Sub 'Prevent the form being closed using Alt+F4 Private Sub UserForm_QueryClose(Cancel As Integer, _ CloseMode As Integer) Cancel = (CloseMode = vbFormControlMenu) End Sub
Displaying Graphics, Charts, WordArt and So Forth on Userforms Userform s have very lim it ed graphics capabilit ies; alt hough we can set t he colors and font s of t he cont rols and use em pt y labels t o draw rect angles, we can't draw diagonal lines, arrows, ovals and so on. Neit her can we em bed ot her obj ect s on t o t he userform t o display chart s, WordArt and so fort h. We can, however, draw our graphics on a worksheet , copy t hem t o t he clipboard and past e t hem as pict ures t o use for t he background of m any of t he MSForm s cont rols. To set t he pict ure, select t he cont rol ( or t he userform it self) , click in t he Pict ure propert y box in t he Propert ies window and eit her click t he ellipsis t o select an im age file or j ust press Ct rl+ V t o past e a pict ure from t he clipboard. Most of t he cont rols st ret ch t he pict ure t o fill t he cont rol, but wit h t he I m age, Fram e and Page cont rols and t he userform background, we can cont rol t he pict ure sizing ( zoom , st ret ch or crop) , alignm ent ( wit hin t he cont rol) and whet her t he pict ure is t iled t o fill t he cont rol. At runt im e, we can use Excel's CopyPict ure m et hod t o copy a range, chart or ot her drawing obj ect t o t he clipboard and our Past ePict ure funct ion t o ret rieve t he im age from t he clipboard as a st andard Pict ure obj ect t hat can be assigned t o t he Pict ure propert y of t he MSForm s cont rols. The funct ion uses lot s of com plex Windows API calls t o ext ract t he pict ure from t he clipboard, so it 's best t o t reat it as a " black box" by j ust copying t he ent ire MPast ePict ure m odule int o your proj ect . We did t his in Chapt er 8 Advanced Com m and Bar Handling t o set a com m and bar but t on's Pict ure and Mask propert ies. The MPast ePict ure m odule can be found in t he Past ePict ure.xls exam ple workbook, which dem onst rat es how t o display a chart on a userform , shown in Figure 10- 4. The relevant part of t he code t o updat e t he chart is shown in List ing 10- 12.
List in g 1 0 - 1 2 . D ispla yin g a Ch a r t on a Use r for m 'Update the chart image on the form Private Sub UpdateChart() Dim chtChart As Chart Dim lPicType As Long 'Find the chart object on the sheet Set chtChart = Sheet1.ChartObjects(1).Chart 'Do we want a metafile or a bitmap? 'If scaling the image, xlPicture will give better results 'If not scaling, xlBitmap will give a 'truer' rendition.
'obMetafile is the 'Metafile' option button on the form lPicType = IIf(obMetafile, xlPicture, xlBitmap) 'Copy the chart to the clipboard, as seen on screen chtChart.CopyPicture xlScreen, lPicType, xlScreen 'Paste the picture from the clipboard into our image control Set imgChtPic.Picture = PastePicture(lPicType) End Sub
Figu r e 1 0 - 4 . D ispla yin g a Ch a r t on a Use r for m [View full size image]
Locking vs. Disabling Controls When t ext boxes and com bo boxes are disabled, Excel displays t he t ext in gray, but keeps t he whit e background. I f t here is no t ext in t he box, t here is no way for t he user t o t ell whet her it is disabled or not . An alt ernat ive is t o keep t he cont rol enabled, but locked and wit h a gray background. Locking a t ext box or com bo box allows t he user t o select any t ext in it , but not change t he t ext . This can be very useful when displaying inform at ion t o t he user t hat t hey m ay want t o copy t o t he clipboard, such as an error m essage. Figure 10- 5 shows a sect ion of a userform cont aining t hree t ext boxesone disabled, one locked wit h a gray background and t he t hird used as a label. I n our opinion, t he m iddle t ext box gives t he best visual indicat or t hat it is disabled, while keeping t he t ext readable. List ing 10- 13 shows t he st andard rout ine t hat we use t o " disable" our cont rols by locking t hem . Unfort unat ely, we cannot use t he sam e t echnique for a list box, because it doesn't redraw t he select ion indicat or when t he background color is changed!
List in g 1 0 - 1 3 . St a n da r d Pr oce du r e t o " D isa ble " a Con t r ol by Lock in g I t 'Enable/Disable a control by locking it and 'changing the background color Public Sub EnableControl(ByRef ctlControl As MSForms.Control, _ ByVal bEnable As Boolean) ctlControl.Locked = Not bEnable ctlControl.BackColor = IIf(bEnable, vbWindowBackground, _ vbButtonFace) End Sub
Figu r e 1 0 - 5 . Th r e e Te x t Box e s
Popup Menus When designing com plex userform s, we have a cont inual t rade- off bet ween adding feat ures t o m ake t he userform easier t o use versus confusing t he user by m aking t he userform t oo clut t ered. For exam ple, if we have a list box wit h a long list of nam es, we could m ake it easier t o find a nam e by adding opt ions t o sort by forenam e or surnam e, in ascending or descending order. We could add t hese cont rols as set s of opt ion but t ons or com bo boxes, but t hose t ake up valuable space on t he form and m ake it appear t oo clut t ered. An alt ernat ive m echanism is t o put t hose opt ions in a com m and bar creat ed wit h t he m soBarPopup st yle, t hen show t hat popup when t he user right clicks t he list box. Figure 10- 6 shows a list box wit h t he popup, and List ing 10- 14 shows t he code t o handle t he right - click and show t he popup. This code assum es t he com m and bar has already been creat ed and ot her rout ines handle t he m enu it em select ions.
List in g 1 0 - 1 4 . Sh ow in g a Popu p for a List Box
'Show a Sort Method popup when the list box is right-clicked Private Sub lstNames_MouseDown(ByVal Button As Integer, _ ByVal Shift As Integer, ByVal X As Single, _ ByVal Y As Single) '2=Right Button If Button = 2 Then Application.CommandBars("NameSortPopup").ShowPopup End If End Sub
Figu r e 1 0 - 6 . A List Box w it h Popu p Sor t M e n u
Userform Positioning and Sizing Positioning Next to a Cell I f we're displaying a userform in response t o t he user select ing a m enu it em from t he cell's popup m enu, it is a nice t ouch t o display t he userform direct ly alongside t he cell ( assum ing t here's space for it on t he screen) . Trying t o work out t he exact posit ion of a cell on t he screen using t he Range's posit ion is ext rem ely difficult , because we would have t o account for t he zoom fact or, scroll set t ings, which t oolbars were displayed and whet her t he Excel applicat ion window is m axim ized. Fort unat ely t here is an easier workaround, which is t o m ake use of t he window t hat Excel uses for edit ing em bedded chart s. I f we creat e a chart obj ect over t he cell and act ivat e it , Excel m oves a window wit h t he class nam e EXCELE t o t hat posit ion. We can im m ediat ely delet e t he chart obj ect , use API funct ions t o read t he posit ion of t he EXCELE window and display our userform in t he sam e place. List ing 10- 15 shows a procedure t o m ove a userform over a cell and an exam ple of it being used t o display a userform alongside of t he act ive cell. You can find t his rout ine on t he CD in t he MForm Pos m odule of Userform Posit ioning.xls. Not e t hat t he rout ine uses funct ions in t he MScreen and MWindows m odules from t he API Exam ples.xls workbook docum ent ed in Chapt er 9 Underst anding and Using Windows API Calls.
List in g 1 0 - 1 5 . Sh ow in g a Use r for m N e x t t o t h e Act ive Ce ll 'API Functions to find a window and read its position Private Declare Function FindWindowEx Lib "user32" _ Alias "FindWindowExA" (ByVal hWnd1 As Long, _ ByVal hWnd2 As Long, ByVal lpsz1 As String, _ ByVal lpsz2 As String) As Long Private Declare Function GetWindowRect Lib "user32" _ (ByVal hWnd As Long, lpRect As RECT) As Long Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type
'Routine to move a form to a given cell Public Sub MoveFormToCell(frmForm As Object, _ rngCell As Range)
Dim hWndDesk As Long Dim hWndChart As Long Dim uChartPos As RECT 'Create a chart object at the cell, activate it and 'immediately delete it. That puts the EXCELE chart 'editing window in the correct place With rngCell.Parent.ChartObjects.Add(rngCell.Left, _ rngCell.Top, 1, 1) .Activate .Delete End With 'Find the EXCELE window hWndDesk = FindWindowEx(ApphWnd, 0, "XLDESK", vbNullString) hWndChart = FindWindowEx(hWndDesk, 0, "EXCELE", vbNullString) 'Read its position GetWindowRect hWndChart, uChartPos 'Move the form to the same position, 'converting pixels to points frmForm.Left = uChartPos.Left * PointsPerPixel frmForm.Top = uChartPos.Top * PointsPerPixel End Sub
'Test procedure to show a form next to the active cell Sub ShowMyForm() Dim frmForm As FMyForm Set frmForm = New FMyForm 'Set the form to show in a custom position frmForm.StartUpPosition = 0 'Move the form over the cell MoveFormToCell frmForm, ActiveCell.Offset(0, 1) 'Show the form frmForm.Show End Sub
Responding to Different Resolutions
We regularly see quest ions in t he Microsoft support newsgroups from people who have designed a userform t o fill t heir screen, only t o find t hat it 's t oo big for t heir users' lower resolut ions. The quest ion usually ends " How do I change t he user's resolut ion t o display m y userform ?" The answer is always " You don't ." I nst ead, we have t o design our userform s so t hey are usable on t he lowest resolut ion our users have. Typically, t hat m eans a resolut ion of 800x600 pixels, alt hough people wit h poor sight or very sm all screens m ay use 640x480 pixels. Designing our userform s t o fit on a 640x480 display gives us t wo m ain issues t o solve:
1 . We can't fit m any cont rols on a 640x480 userform . 2 . Userform s t hat fit on a 640x480 screen oft en m ake very poor use of t he space available wit h larger resolut ions. I n pract ice, m ost of t he userform s we creat e are quit e sim ple and can usually fit wit hin t he bounds of a 640x480 screen. For com plex form s, we usually use popup m enus, drop- down panes ( see lat er) and/ or a wizard st yle t o m ake t he m ost of t he available space and m ay design m ult iple versions of t he sam e form , for use wit h different screen resolut ions. The form s for lower resolut ions will use m ore com pact cont rols, such as com bo boxes inst ead of set s of opt ion but t ons or list boxes and have less blank space around each cont rol, whereas t he form s for higher resolut ions will have m ore cont rols direct ly visible, wit h each cont rol using m ore space. I f we correct ly split our code bet ween t he form layer and business logic layer, bot h form s can use t he sam e class for t heir business logic.
Resizable Userforms Part of t he KI SS principle is avoiding overwhelm ing t he user. Experience has shown us t hat if a userform won't fit on an 800x600 resolut ion screen, it alm ost cert ainly cont ains t oo m any cont rols. For t hat reason, we design our form s t o fit on an 800x600 screen, but m ake t hem resizable so t he user can choose t o m ake bet t er use of t he space available if t hey have a higher- resolut ion screen. For exam ple, if our userform includes a list box, we allow t he list box t o change size wit h t he form , t hereby allowing t he user t o see m ore it em s in t he list . The Form Resizer.xls exam ple workbook cont ains a class m odule, CForm Resizer, which can be included in a proj ect t o handle t he resizing of any form . The class changes t he form 's window st yles t o m ake it resizable and handles t he resizing and reposit ioning of all t he cont rols on t he form . We define t he resize behavior of each cont rol by set t ing it s Tag propert y t o indicat e by how m uch each of it s t op, left , height and/ or widt h should change in proport ion t o t he change in size of t he form . To m ake one of t he propert ies change as t he form is sized, we include t he let t er T, L, H, or W followed by a num ber giving t he percent age change ( or om it t ed for 100 percent ) . For exam ple, if we have an OK but t on in t he m iddle bot t om of t he form , we would want it t o m ove up/ down t he sam e am ount as t he change in t he form 's height and m ove left / right by half t he change in t he form 's widt h; it s Tag would be TL0.5. I f we have a form wit h a pair of list boxes side by side, we would want t he left list box t o keep it s t op and left const ant , but grow by t he full change in t he form 's height and half t he change in t he form 's widt h; it s Tag would be HW0.5. The right - hand list box would resize t he sam e way, but should also m ove across by half t he change in form 's widt h ( so it s right edge st ays const ant relat ive t o t he right edge of t he form ) ; it s Tag would be L0.5HW0.5. To st art including resizable userform s in your applicat ions, copy t he CForm Resizer class int o t he proj ect , hook it up t o a form using t he code shown in List ing 10- 16 and set t he cont rols' Tag
propert ies appropriat ely. I t will probably t ake som e t rial and error t o get t he t ags correct at first , but will becom e m uch easier wit h pract ice. For best result s, list boxes should have t heir I nt egralHeight propert y set t o False, and due t o an Excel bug, t hey m ay need an ext ra blank it em added t o t he bot t om of t he list for all t he it em s t o display correct ly.
List in g 1 0 - 1 6 . M a k in g a Use r for m Re siza ble Usin g t h e CFor m Re size r Cla ss 'Declare an object of our CFormResizer class to handle 'resizing for this form Dim mclsResizer As CFormResizer 'The Resizer class is set up in UserForm_Initialize Private Sub UserForm_Initialize() 'Create the instance of the class Set mclsResizer = New CFormResizer 'Tell it which form it's handling Set mclsResizer.Form = Me End Sub 'When the form is resized, the UserForm_Resize event is 'raised, which we just pass on to the Resizer class Private Sub UserForm_Resize() mclsResizer.FormResize End Sub 'The QueryClose event is called whenever the form is closed. 'We call the FormResize method one last time, to store the 'form's final size and position in the registry Private Sub UserForm_QueryClose(Cancel As Integer, _ CloseMode As Integer) mclsResizer.FormResize End Sub
Splitter Bars I f our resizable userform s cont ain t wo or m ore list boxes, it m ay not be always desirable t o let t hem bot h grow or shrink at t he sam e rat e. We can allow our users t o decide how m uch space t o give each form by adding a split t er bar bet ween t hem . We don't act ually have a split t er bar cont rol, but we can fake one using a norm al Label. The userform shown in Figure 10- 7 has t wo list boxes t hat are bot h configured for t heir widt h t o change at half t he rat e of t he form 's change in widt h, keeping t he gap bet ween t hem cent ral t o t he form . We've also added a label t o fill t he gap bet ween
t he list boxes. For clarit y, it is shown here wit h it s nam e, but would norm ally be t ransparent and blank. The label's MousePoint er propert y has been changed t o fm MousePoint erSizeWE, so we get t he st andard left / right sizing arrows when t he m ouse m oves over t he label. The code in List ing 1017 uses t he label's m ouse event s t o sim ulat e a split t er bar.
List in g 1 0 - 1 7 . Th e Code t o Tu r n a La be l in t o a Split t e r Ba r 'Module variables to handle the splitter bar Dim mbSplitterMoving As Boolean Dim mdSplitterOrigin As Double 'When pressing down the left mouse button, 'initiate the dragging and remember where we started Private Sub lblSplitterBar_MouseDown( _ ByVal Button As Integer, ByVal Shift As Integer, _ ByVal X As Single, ByVal Y As Single) If Button = 1 Then mbSplitterMoving = True mdSplitterOrigin = X End If End Sub 'When releasing the left mouse button, 'stop the dragging Private Sub lblSplitterBar_MouseUp( _ ByVal Button As Integer, ByVal Shift As Integer, _ ByVal X As Single, ByVal Y As Single) If Button = 1 Then mbSplitterMoving = False End Sub 'When moving the mouse over the label 'and we're in 'drag' mode (i.e. dragging the splitter), 'move all the controls appropriately Private Sub lblSplitterBar_MouseMove( _ ByVal Button As Integer, ByVal Shift As Integer, _ ByVal X As Single, ByVal Y As Single) Dim dChange As Double 'Are we doing a drag? If mbSplitterMoving Then 'Find where we moved to dChange = (X - mdSplitterOrigin) / PointsPerPixel 'Adjust the control sizes and positions If (lstLeft.Width + dChange > 0) And _
(lstRight.Width - dChange > 0) Then 'The left list changes size lstLeft.Width = lstLeft.Width + dChange 'The splitter bar in the middle moves lblSplitterBar.Left = lblSplitterBar.Left + dChange 'The right list moves and changes size lstRight.Left = lstRight.Left + dChange lstRight.Width = lstRight.Width - dChange End If End If End Sub
Figu r e 1 0 - 7 . A Split t e r Ba r Be t w e e n Tw o List Box e s
Wizards Wizard dialogs are norm ally used when we need t o collect a reasonably large am ount of dat a from t he user. The only absolut e requirem ent t his dat a m ust fulfill in order t o be a candidat e for a wizard dialog is t hat t he bit s of dat a being collect ed m ust be logically relat ed t o each ot her in som e way. Wizard dialogs are part icularly useful where t he dat a being collect ed has t he following charact erist ics in addit ion t o being logically relat ed: The inform at ion is com plex and varied. The inform at ion m ust be supplied in a defined order because earlier select ions alt er t he allowable param et ers of lat er select ions. The user does not need t o underst and t he relat ionship bet ween earlier and lat er choices. The wizard dialog can t hen abst ract t his decision- m aking process away from t he user. The prim ary purpose of a wizard dialog is t o reduce t he num ber of choices t he user m ust m ake at any one t im e t o a m anageable level. An im port ant secondary purpose of a wizard dialog is t o allow us t o alt er t he part s of t he user int erface t hat depend on t he select ions t he user is current ly m aking wit hout having t o do so in a way t hat is visible t o t he user and t hereby pot ent ially dist ract t hem from t he t ask at hand ( see Dynam ic Userform s lat er for an exam ple) .
Design Rules for Wizard Dialogs
1 . The first page of a wizard dialog should explain t he purpose of t he wizard and t he st eps involved, but always have a " Don't show m e t his again" check box t o aut om at ically skip t he first page if t he user want s. 2 . The last page of a wizard dialog should confirm everyt hing t he user has ent ered and all t he choices m ade, and no act ions are perform ed unt il t he user clicks t he Finish but t on. 3 . Always display t he st ep num ber wit hin t he wizard t hat t he user is current ly working on as well as t he t ot al num ber of st eps left t o com plet e. This inform at ion is t ypically displayed in t he t it le bar, alt hough we've seen perfect ly accept able designs t hat display it elsewhere. 4 . Navigat ion t hrough t he wizard is t ypically cont rolled by a series of four but t ons: Cancel, Back, Next and Finish. The enabled st at e of t hese but t ons should be used t o provide visual clues t o t he user about how t hey're doing. Track t he user's progress t hrough t he wizard and wat ch t heir input during each st ep of t he wizard. Based on where t he user is and what dat a he has ent ered, enable only t he navigat ion but t ons t hat m akes sense, such as t he following: First st ep wit h no dat a ent eredThe Cancel but t on should be t he only but t on enabled.
( Cancel is always enabled.) The Next but t on is only enabled when t he page passes all validat ion checks, as long as t he user can det erm ine which are t he invalid ent ries and why ( see Validat ion earlier) . Last st ep wit h all dat a ent ered and validat edCancel enabled, Back enabled, Next disabled and Finish enabled. The user has com plet ed all wizard st eps correct ly but t hen used t he Back but t on t o revisit an earlier st epAll but t ons enabled unt il t he user m akes an ent ry t hat invalidat es t he abilit y of t he wizard t o finish or m ove forward. I n, say, a five- st ep wizard, if st eps four and five allow t he user t o ent er opt ional inform at ion, t he Finish but t on can be enabled aft er st ep t hree. Excel's Chart Wizard is a good exam ple of t his.
5 . The user can m ove back and fort h t hrough wizards t o his heart 's cont ent . Therefore, you m ust always keep t rack of t he st at us of all st eps in t he wizard in order t o properly set t he st at us of t he navigat ion but t ons. I t is perfect ly appropriat e for t he user t o click Finish from st ep t wo of a five- st ep wizard as long as he has com plet ed all five st eps and has j ust m oved back t o st ep t wo in order t o m ake a m inor change. 6 . I n som e wizard designs, select ions m ade on a st ep affect ot her select ions on t hat sam e st ep. I f a select ion on a st ep m akes anot her select ion on t hat sam e st ep unnecessary, do not h id e t he cont rols for t he unnecessary select ion. Just disable t hem . Cont rols t hat pop in and out of exist ence in front of t he user's face t end t o be a confusing dist ract ion.
Creating a Wizard Dialog The easiest way t o creat e a wizard dialog is t o use a Mult iPage cont rol, wit h each page of t he cont rol being used for a separat e st ep of t he wizard and a com m on set of but t ons at t he bot t om . Figure 10- 8 shows t he wizard userform t em plat e included on t he CD in t he WizardDem o.xls workbook, wit h t he Mult iPage t abs showing on t he right side. Prior t o dist ribut ing t he wizard, t he Mult iPage should be form at t ed t o not have any t abs showing by set t ing it s St yle propert y t o fm TabSt yleNone, and reducing bot h t he Mult iPage's and userform 's widt h accordingly.
Figu r e 1 0 - 8 . An Em pt y W iza r d Use r for m Usin g a M u lt iPa ge Con t r ol for t h e St e ps
Unfort unat ely, t he Mult iPage cont rol is not wit hout it s problem s, part icularly when using nonMSForm s cont rols wit hin a page. I f you int end t o use t he RefEdit cont rol or any of t he Windows Com m on Cont rols ( such as t he TreeView and List View cont rol) , you should use a separat e Fram e cont rol for each st ep of t he wizard inst ead of a Mult iPage cont rol. I f using a Fram e cont rol, it 's easiest t o develop t he wizard wit h all t he fram es visible at t he sam e t im e, on a userform m uch larger t han t he final version. When t he wizard is com plet e, change t he fram es' left and t op so t hey all overlap and reduce t he userform t o it s correct size. List ing 10- 18 shows t he code for t he four navigat ion but t ons, which each call furt her procedures t o init ialize and validat e t he cont rols in each st ep. The cont ent of t he I nit ializeSt ep and bValidat eSt ep procedures will obviously depend on t he cont ent s of t he st ep, so have not been shown here. As well as init ializing t he cont rols on each page, t he I nit ializeSt ep procedure should updat e t he userform 's capt ion t o show t he st ep num ber and enable/ disable t he navigat ion but t ons.
List in g 1 0 - 1 8 . Th e N a viga t ion Code for a W iza r d D ia log Private Sub cmdCancel_Click() mbUserCancel = True Me.Hide End Sub Private Sub cmdBack_Click() ' Can't go back from step 1. If mlStep > 1 Then ' No validation is required when moving back. mlStep = mlStep - 1
mpgWizard.Value = mlStep - 1 InitializeStep mlStep End If End Sub Private Sub cmdNext_Click() ' Can't go forward from the last step. If mlStep 5 Then lBoxes = 5 'Initialize the collection of event handler classes Set mcolEvents = New Collection 'Create the required number of boxes
For lBox = 1 To lBoxes 'Add a label to the form Set lblLabel = Me.Controls.Add("Forms.Label.1", _ "lbl" & lBox) With lblLabel .Top = (lBox - 1) * 21.75 + 9 .Left = 6 .Width = 50 .Height = 9.75 .WordWrap = False .Caption = "Text Box " & lBox End With 'Add the text box to the form Set txtBox = Me.Controls.Add("Forms.TextBox.1", _ "txt" & lBox) With txtBox .Top = (lBox - 1) * 21.75 + 6 .Left = 56 .Width = 50 .Height = 15.75 End With 'Create a new instance of the event handler class Set clsEvents = New CTextBoxEvents 'Tell it to handle the events for the text box Set clsEvents.Control = txtBox 'Add the event handler instance to our collection, 'so it stays alive during the life of the form mcolEvents.Add clsEvents Next End Sub
We can use t he sam e t echnique t o handle t he event s of cont rols in nondynam ic userform s as well. I m agine a form wit h 50 t ext boxes, all requiring num eric validat ion. We could include all 50 Change event procedures in our code and accept t he m aint enance overhead t hat brings, or we could use t he class m odule from List ing 10- 20 t o handle t he validat ion for all our t ext boxes. The code in List ing 10- 22 it erat es t hrough all t he cont rols on t he form , hooking up new inst ances of t he event handler class for every t ext box it finds.
List in g 1 0 - 2 2 . Cla ss t o H a n dle a Te x t Box 's Eve n t s
'Collection to store instances of our event handler class Dim mcolEvents As Collection 'Hook the events for all the Text Boxes Private Sub UserForm_Initialize() Dim ctlControl As MSForms.Control Dim clsEvents As CTextBoxEvents 'Initialize the collection of event handler classes Set mcolEvents = New Collection 'Loop through all the controls For Each ctlControl In Me.Controls 'Check if it's a text box If TypeOf ctlControl Is MSForms.TextBox Then 'Create a new instance of the event handler class Set clsEvents = New CTextBoxEvents 'Tell it to handle the events for the text box Set clsEvents.Control = ctlControl 'Add the event handler instance to our collection, 'so it stays alive during the life of the form mcolEvents.Add clsEvents End If Next End Sub
Modeless Userforms Most of t he dialogs t hat we norm ally com e int o cont act wit h are m odal, which is t o say t hat neit her t he applicat ion nor t he user can do anyt hing unt il t he form is dism issed. When t he Show st at em ent is processed, by default t he applicat ion window is disabled ( so none of t he m enus are available) , t he form is displayed and t he code st ops. Snippet s of code can run in response t o cont rol event s, but it 's not unt il t he user closes t he form t hat execut ion cont inues on t he line aft er t he Show st at em ent . When a userform is shown m odeless, however, code execut ion cont inues im m ediat ely aft er t he Userform _I nit ialize and Userform _Act ivat e event procedures have finished, wit h t he userform rem aining displayed. I f t he code com es t o an end while a m odeless userform is displayed, t he form rem ains open and bot h t he userform and t he applicat ion window can be used.
N OTE Excel 97 does not support m odeless userform s.
Splash Screens The sim plest use for a m odeless userform is as an int roduct ory splash screen. The userform is shown m odeless at t he st art of t he Aut o_Open or Workbook_Open procedure and unloaded at t he end of t he procedure. List ing 10- 23 shows a sim ple exam ple, where t he form uses t he Set Userform Appearance procedure from earlier t o rem ove t he t it le bar.
List in g 1 0 - 2 3 . Sh ow in g a Spla sh Scr e e n a t St a r t u p Sub Auto_Open() Dim frmSplash As FSplashScreen 'Show the form modelessly Set frmSplash = New FSplashScreen frmSplash.Show vbModeless 'Process the startup code Application.Wait Now + TimeValue("00:00:5") 'Unload the splash screen
Unload frmSplash Set frmSplash = Nothing End Sub 'The FSplashScreen Userform's Code Module Option Explicit 'Set the form to have no title bar Private Sub UserForm_Initialize() 'Adjust the height for the missing caption Me.Height = Me.InsideHeight SetUserformAppearance Me, uwsNoTitleBar End Sub 'Prevent the form being closed using Alt+F4 Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) Cancel = (CloseMode = vbFormControlMenu) End Sub
Progress Bars A rat her m ore int erest ing use of m odeless form s is t o display progress inform at ion t o t he user during lengt hy looping operat ions. Figure 10- 11 shows a sim ple progress bar userform , where t he progress indicat or is m ade up of t wo overlapping Fram e cont rols, each cont aining a label. The back fram e has a whit e background and a label wit h blue t ext , and t he front fram e has a blue background and a label wit h whit e t ext . As t he progress is updat ed, t he widt h of t he front fram e is adj ust ed, allowing us t o see m ore of t he blue background. This m akes t he bar appear t o fill up as t he progress increases.
Figu r e 1 0 - 1 1 . A M ode le ss Pr ogr e ss Ba r
The code for t he progress bar form is t oo lengt hy t o show here, but is included on t he CD in t he ModelessForm s.xls exam ple workbook. The FProgressBar form can be copied from t he exam ple workbook int o your proj ect and cont rolled using code such as t hat shown in List ing 10- 24.
List in g 1 0 - 2 4 . Usin g t h e Pr ogr e ss Ba r Use r for m Sub ShowProgress() Dim lLoop As Long Dim lIterations As Long Dim frmProgress As FProgressBar lIterations = 2000 'Initialize the progress bar Set frmProgress = New FProgressBar frmProgress.Title = "Professional Excel Development" frmProgress.Text = "Preparing reports, please wait..." frmProgress.Min = 1 frmProgress.Max = lIterations 'Show the progress bar frmProgress.ShowForm For lLoop = 1 To lIterations 'Check if the user cancelled If frmProgress.Cancelled Then Exit For 'Update the progress frmProgress.Progress = lLoop 'Do Stuff Next lLoop 'Unload the progress bar form Unload frmProgress End Sub
Combining with Menu Items I f we display a m odeless userform and t hen allow our code t o finish, t he form is left act ive on t he screen, and bot h t he form and t he applicat ion can be used. This behavior can be used t o very good effect in form - based dict at or applicat ions. I n t his design, t he worksheet is only ever used for a
backdrop graphic display; all t he int eract ion wit h t he user is done t hrough userform s. Most form based applicat ions have a cent ral " swit chboard" form , wit h a set of but t ons t o show subform s for each funct ional area. Those form s have t heir own but t ons t o show ot her form s and so on. I t is usually very difficult t o navigat e around t he applicat ion. I f we use m odeless userform s, however, t he m enus are available, so we can im plem ent a m enu st ruct ure t hat enables t he user t o quickly swit ch bet ween part s of t he applicat ion. To im plem ent t his design, we need t o be able t o com m unicat e wit h all t he form s, so t hey can be not ified when t he user clicks a m enu it em t o j um p t o anot her form , or when t he applicat ion is about t o exit , or if t he Save m enu it em is clicked. All t he form s will have t o include t he sam e set of st andard funct ions, shown in List ing 10- 25, t hat can be called from a cent ral " form - handler" r out ine.
List in g 1 0 - 2 5 . St a n da r d Rou t in e s t o Be I n clu de d in All M ode le ss For m s ' Called prior to navigating to another form. ' Allows the form to validate and store its data, then unload ' If validation fails, the navigation can be cancelled Public Sub BeforeNavigate(ByRef Cancel As Boolean) End Sub ' Called prior to saving the data workbook ' Allows the form to validate and store its data ' If validation fails, the navigation can be cancelled Public Sub BeforeSave(ByRef Cancel As Boolean) End Sub ' Called after to saving the data workbook ' Allows the form to update its display ' (e.g. if showing the file name) Public Sub AfterSave() End Sub ' Called when the application is about to be closed ' The form should unload itself, but could cancel the close Public Sub AppExit(ByRef Cancel As Boolean) End Sub
Wit h all t he userform s having t he sam e set of st andard rout ines, we can writ e a sim ple cent ralized rout ine t o m anage t hem all, shown in List ing 10- 26.
List in g 1 0 - 2 6 . Th e Ce n t r a l Con t r ol Rou t in e t o H a n dle N a viga t ion Be t w e e n For m s ' Global variable to hold the form currently being displayed
Dim gfrmActiveForm As Object ' A single OnAction procedure for most menu items, where the ' form name is obtained from the menu item's Parameter Sub FormMenuClick() ShowForm Application.CommandBars.ActionControl.Parameter End Sub 'Common routine to switch between forms Sub ShowForm(ByVal sForm As String) Dim bCancel As Boolean 'If there's an active form, tell it to save and unload If Not gfrmActiveForm Is Nothing Then gfrmActiveForm.BeforeNavigate bCancel End If 'If the save/close wasn't cancelled, If Not bCancel Then 'Show the next form, assuming it is in the same workbook Set gfrmActiveForm = VBA.Userforms.Add(sForm) gfrmActiveForm.Show vbModeless End If End Sub 'The OnAction routine for the File > Save menu item Sub MenuFileSave() Dim bCancel As Boolean 'If there's an active form, tell it to save its data If Not gfrmActiveForm Is Nothing Then gfrmActiveForm.BeforeSave bCancel End If If Not bCancel Then 'Save the data workbook if not cancelled gwkbDataWorkbook.Save 'If there's an active form, tell it to do its post-save 'processing (if any) If Not gfrmActiveForm Is Nothing Then gfrmActiveForm.AfterSave End If End If End Sub
Using t his m echanism , we can add m ore userform s t o t he applicat ion wit hout having t o add any
ext ra code t o cont rol t heir display; as long as t hey include t he st andard set of procedures shown in List ing 10- 25, t hey will plug in t o t he cent ral cont rol procedure. All we need t o do is add t he form m odule t o t he workbook and add som e ext ra lines t o t he t able used by t he com m and bar builder, t o include t he new form in our applicat ion's m enu st ruct ure.
Control Specifics Most of t he cont rols t hat we use in our form s are well docum ent ed and well underst ood, so docum ent ing t hem here would be of lit t le benefit t o t he reader. I nst ead, t his sect ion of t he chapt er explains how t o use som e of t he lesser- known cont rols, or how t o use t hem in innovat ive ways.
ComboBox The Com boBox is t he unsung hero of t he MSForm s t oolbox. By changing t he st yle of t he drop- down but t on, we can use a com bo box as a norm al drop- down list , as a t ext box, as a filenam e ent ry box or as a t ot ally cust om ized drop- down cont rol. Figure 10- 12 shows four com bo boxes, wit h t he bot t om one shown in it s dropped st at e, revealing a cust om drop- down pane for specifying a filt er.
Figu r e 1 0 - 1 2 . Com bo Box St yle s
Drop-Down List/Combo The default behavior for a com bo box is t o allow t he user t o select an it em from a list or t ype in ent ries t hat do not exist in t he supplied list ; when t he arrow is clicked, t he list is shown below t he com bo box. When t he user clicks on an it em from t he list , t he select ed it em is shown in t he cont rol and t he list is hidden.
Text Box I f we set t he ShowDropBut t onWhen propert y t o fm ShowDrop- But t onNever and t he St yle propert y t o fm St yleDropDownCom bo, t he result is a cont rol t hat looks and behaves exact ly like a t ext box. This allows us t o have a single cont rol t hat can be used t o select an it em from a list , or allow direct ent ry. For exam ple, in t he userform in Figure 10- 12, t he t op drop- down m ay be a list of at t ribut es about a person, such as age, sex and so on, whereas t he second drop- down would be used t o fill in t he value for t he select ed at t ribut e. When Age is select ed from t he drop- down, we would want t o be able t o t ype a num ber direct ly int o t he cont rol, but when Sex is select ed, we would want t o show a drop- down t o choose bet ween Male and Fem ale.
Filename Box By changing t he DropBut t onSt yle t o fm DropBut t onSt yleEllipsis, we creat e a cont rol t hat looks like a filenam e box. The user would expect a File Open dialog t o appear when he clicks t he but t on. We can do exact ly t hat by hooking t he DropBut t onClick event , as shown in List ing 10- 27.
List in g 1 0 - 2 7 . H a n dle t h e Ellipsis in t h e File n a m e Com bo 'Handle clicking the ellipsis in the Filename combo Private Sub cboFileName_DropButtonClick() Dim vFile As Variant 'Get the filename vFile = Application.GetOpenFilename() 'Write it to the control If TypeName(vFile) = "String" Then cboFileName.Text = vFile End If 'Toggle the Enabled property to move the focus 'to the next control cboFileName.Enabled = False cboFileName.Enabled = True End Sub
One annoying aspect of hooking t he DropBut t onClick event is t hat we can't cancel it , so t he cont rol shows an em pt y list aft er we've obt ained t he filenam e. One workaround for t his is t o t oggle t he Enabled propert y of t he cont rol, which forces t he focus t o m ove t o t he next cont rol in t he t ab order.
Drop-Down Panes The fourt h com bo box shown in Figure 10- 12 is used t o im plem ent a t ot ally cust om ized drop- down pane, t o display a sim ple filt er select ion. This would t ypically be used above a list box, t o filt er t he it em s in t he list . The code t o handle t he filt er pane is shown in List ing 10- 28.
List in g 1 0 - 2 8 . Code t o M a n a ge a Cu st om D r op- D ow n Pa n e 'Boolean to identify if the filter has changed Dim mbFilterChanged As Boolean 'Set up the form Private Sub UserForm_Initialize() cboFilter.AddItem "All Clients" cboFilter.ListIndex = 0 cboConsultant.List = Array("Stephen Bullen", "Rob Bovey", _ "John Green") cboConsultant.ListIndex = 0 End Sub 'When clicking the drop-down, show the filter frame Private Sub cboFilter_DropButtonClick() mbFilterChanged = False fraFilter.Visible = True fraFilter.SetFocus End Sub 'Changing any of the filter options 'sets the 'Filter Changed' Boolean Private Sub optAllClients_Click() mbFilterChanged = True cboConsultant.Enabled = optClientsForConsultant.Value End Sub Private Sub optClientsForConsultant_Click() mbFilterChanged = True cboConsultant.Enabled = optClientsForConsultant.Value End Sub Private Sub cboConsultant_Change() mbFilterChanged = True End Sub 'When exiting the frame, check for updates to the filter Private Sub fraFilter_Exit(ByVal Cancel As _ MSForms.ReturnBoolean)
CheckFilterFrame End Sub 'When clicking outside the frame, 'check and close the filter panel Private Sub UserForm_MouseDown(ByVal Button As Integer, _ ByVal Shift As Integer, ByVal X As Single, _ ByVal Y As Single) CheckFilterFrame End Sub 'Handle clicking outside the frame, 'to check for updates and close the panel Private Sub CheckFilterFrame() 'If it's visible, update the list If fraFilter.Visible Then If mbFilterChanged Then ApplyFilter End If fraFilter.Visible = False End Sub 'Apply the changed filter options Private Sub ApplyFilter() 'Update the text of the filter dropdown If optAllClients Then cboFilter.List(0) = "All Clients" Else cboFilter.List(0) = "Clients for " & cboConsultant.Text End If 'Update the contents of the list box End Sub
The cust om drop- down pane is a st andard Fram e cont rol, init ially set t o be invisible and wit h a slight ly light er background color, cont aining t he cont rols used for our filt er. When t he user clicks t he com bo box's drop- down but t on, we use t he DropBut t onClick event t o init ialize a Boolean variable m bFilt erChanged ( used t o ident ify whet her changes are m ade wit hin t he fram e) , t hen m ake t he fram e visible and give it t he focus; t his m akes t he fram e appear t o " drop down" from t he com bo box. We include code in t he change event for all t he cont rols in t he fram e t o set t he Boolean variable t o True, indicat ing t hat t he fram e's cont ent has changed. The user can exit t he fram e by t abbing t o or clicking anot her cont rol ( causing t he Exit event t o fire) , or by clicking som ewhere else on t he userform ( which we det ect wit h t he Userform _MouseDown event ) . I n bot h cases, we call t he CheckFilt erFram e procedure t o hide t he fram e, check whet her any of t he cont rols were changed and apply t he new filt er. I n t his case, we're j ust updat ing t he t ext shown in t he com bo box t o show t he filt er set t ings. The com bo box st yle is set t o fm St yleDropDownList , so t hat clicking anywhere in t he com bo box will cause t he DropBut t onClick event t o fire. We have a single it em in t he com bo
box's list , wit h t he List I ndex t o zero t o show it in t he cont rol. To updat e t he t ext shown in t he com bo box, we change t he t ext of t hat it em .
Windows Common Controls There is an OCX file available on m ost com put ers called m scom ct l.ocx, usually found in t he C: \ w indow s\ syst em 32 folder t hat cont ains a set of cont rols collect ively known as t he Microsoft Windows Com m on Cont rols 6.0. Alt hough it t heoret ically m ight not exist , we have yet t o see a com put er wit h Office inst alled t hat doesn't have t his fileit is so widely used t hat anyt hing ot her t han a plain vanilla Windows inst allat ion will include t he file. I t cont ains t he following cont rols t hat can be used in our userform s. To access t hese cont rols, right - click t he cont rol t oolbox, select t he Addit ional Cont rols, m enu and put a t ick m ark beside each of t he cont rols you int end t o use: Microsoft I m ageCom boBox Cont rol 6.0 Microsoft I m ageList Cont rol 6.0 Microsoft List View Cont rol 6.0 Microsoft ProgressBar Cont rol 6.0 Microsoft Slider Cont rol 6.0 Microsoft St at usBar Cont rol 6.0 Microsoft TabSt rip Cont rol 6.0 Microsoft ToolBar Cont rol 6.0 Microsoft TreeView Cont rol 6.0 Microsoft UpDown Cont rol 6.0 Som e of t hese cont rols, such as t he TabSt rip and UpDown cont rols, are very sim ilar t o t he st andard MSForm s cont rols, but t he ot hers can be used t o im prove t he usabilit y of our form s. For exam ple, t he List View is sim ilar t o t he File pane of Windows Explorer, and enables us t o display a list of it em s wit h icons, giving us m any form at t ing possibilit ies for each it em in t he list . The List View's " report " st yle is very sim ilar in appearance t o t he norm al List cont rol, but enables us t o display each it em using a different font , color and so fort h. The TreeView cont rol is an excellent way t o display hierarchical dat a, and t he I m ageList and I m ageCom bo cont rols can be used where displaying t hum bnails m ay be m ore appropriat e t han t ext . To fully docum ent each of t he Windows Com m on Cont rols is beyond t he scope of t his book, but t he Com m onCont rols.xls exam ple workbook cont ains t he userform shown in Figure 10- 13, wit h fully com m ent ed code t o explain it s operat ion.
Figu r e 1 0 - 1 3 . Usin g t h e W in dow s Com m on Con t r ols
[View full size image]
The official docum ent at ion for t he Windows Com m on Cont rols can be found in t he MSDN library wit hin t he Visual Basic 6.0 sect ion. For exam ple, t he docum ent at ion for t he TreeView st art s at ht t p: / / m sdn.m icr osoft .com / libr ar y/ en- us/ cm ct l198/ ht m l/ vbobj Tr eeView .asp. The only issue t o be aware of when using t he Windows Com m on Cont rols on userform s is t hat t hey do not like t o be placed inside t he Mult iPage cont rol. Trying t o m odify a cont rol t hat is not on t he act ive page usually fails.
Drag and Drop The norm al MSForm s cont rols do not support drag- and- drop operat ions bet ween cont rols. I f we want t o im plem ent drag and drop bet ween cont rols on our form s ( such as being able t o drag an it em from one list box and drop it on anot her) , we have t o eit her use t he Windows Com m on Cont rols or use a Visual Basic form . The Com m onCont rols.xls workbook cont ains fully com m ent ed code t hat im plem ent s drag and drop bet ween t he List View and TreeView cont rols as well as wit hin t he TreeView cont rol t o change it s st ruct ure.
Practical Examples PETRAS Timesheet The PETRAS t im esheet add- in has not been changed for t his chapt er.
PETRAS Reporting At t his st age of t he PETRAS report ing applicat ion, it would be art ificial t o add a suit e of userform s j ust t o dem onst rat e t he t echniques discussed in t his chapt er. Therefore, t he only change m ade t o t he report ing applicat ion for t his chapt er is t o display a progress bar while consolidat ing all t he t im esheet workbooks. When we m odify t he applicat ion t o use a dat abase back end in Chapt er 13 Program m ing wit h Dat abases, we will add a set of userform s t o t he report ing applicat ion for t he user t o m aint ain t he st at ic list s of consult ant s, client s, proj ect s and so on. The code changes required t o add t he progress bar are det ailed in Table 10- 1 .
Ta ble 1 0 - 1 . Ch a n ge s t o t h e PETRAS Re por t in g Applica t ion for Ch a pt e r 1 0 M odu le FProgressBar ( new form ) MSyst em Code
Pr oce du r e
Ch a n g e Added t he FProgressBar form shown in Figure 1011
Consolidat eWor k book s Modified t o use t he FProgressBar form inst ead of writ ing t he progress t o t he st at us bar
Conclusion As program m ers, we t end t o t hink t hat our code is t he m ost im port ant part of an applicat ion and dism iss t he userform s as m ere eye candy. Our users, on t he ot her hand, don't see t he code, only t he int erface t hat we provide t hem . The userform s are t he m ost im port ant part of our applicat ion t o t he users, and t hey will like or dislike our applicat ions based prim arily on how well t he userform s are designed. For t his reason and m ore, userform design should be as m uch a priorit y as any ot her part of our applicat ion developm ent . Taking t he t im e t o design userform s t hat are easy t o use, easy t o m aint ain and t hat adapt t o t he user's environm ent can give our workbook t he polished appearance expect ed of a professionally developed applicat ion.
Chapter 11. Interfaces The previous few chapt ers have explained class m odules in som e dept h and t he various part s t hey can play in our applicat ionshandling event s, encapsulat ing funct ionalit y and creat ing our own obj ect m odels. We've seen t hat userform s and t he workbook and worksheet code m odules are j ust special t ypes of class m odule. This chapt er t akes a st ep furt her int o obj ect - orient ed design by explaining how one class can appear t o be m any different t ypes of obj ect and how m any different classes can appear t o be t he sam e t ype of obj ect . We show t hat by using t he t echniques explained in t his chapt er, we can im prove t he robust ness of our solut ion, sim plify t he developm ent experience and reduce t he am ount of code we need t o writ e. As an exam ple, we convert our cent ral consolidat ion and report ing applicat ion t o use a plug- in archit ect ure for it s userform s, enabling us t o ext end t he applicat ion wit hout having t o add any m ore code t o t he core rout ines.
What Is an Interface? An int erface is a list of public propert ies, m et hods, event s, user- defined- t ypes, const ant s and/ or enum erat ions t hat we can use t o int eract wit h an obj ect . When we dim ension a variable t o be a cert ain obj ect t ype, we're act ually specifying t he in t e r fa ce t hat t he variable will use t o t alk t o an obj ect . When we lat er m ake t he variable refer t o an obj ect , we're specifying which ob j e ct we want t o t alk t o, t hrough t hat int erface. When t he code is run, t he com piler checks t o see whet her t he obj ect has t he int erface we specified, and t hrows a Type Mism at ch error if it doesn't , as shown in List ing 11- 1.
List in g 1 1 - 1 . A Type M ism a t ch Er r or 'Declare a variable that will talk to objects through the 'Worksheet interface Dim wksInput As Worksheet 'Sheet1 in our workbook has the Worksheet interface, 'so we can talk to it Set wksInput = Sheet1 'The ThisWorkbook object doesn't have the Worksheet interface, 'so we get a Type Mismatch error. Set wksInput = ThisWorkbook
Whenever we creat e a class m odule, t he VBA com piler also creat es a default int erface for t hat class. The default int erface is given t he sam e nam e as t he class and cont ains a list of all t he public propert ies, m et hods et c. t hat we add t o t he class. When we dim ension a variable using Dim clsTheClass As CClassName, we're saying t hat t he variable will use t he in t e r fa ce CClassNam e. When we use code like Set clsTheClass = New CClassName, we're creat ing an obj ect t hat is a new inst ance of t he cla ss CClassNam e, t hen set t ing t he variable t o refer t o t he obj ect , as in List ing 11- 2 .
List in g 1 1 - 2 . Va r ia ble s, I n t e r fa ce s a n d Cla sse s 'Declare a variable to use the CClassName interface Dim clsTheClass As CClassName 'Create a new instance of the CClassName class 'and set our variable to refer to it Set clsTheClass = New CClassName
Th e code in t h e cla ss de fin e s h ow t h e obj e ct be h a ve s, w h e r e a s t h e in t e r fa ce de fin e s h ow w e a cce ss t h e code . By hiding t his im plem ent at ion det ail from us, VBA m akes it m uch easier for us t o work wit h class m oduleswe don't need t o care whet her we're dealing wit h a class or an int erface. Unfort unat ely, it also hides t he useful fact t hat we can define our own cust om int erfaces and m ix and m at ch classes and int erfaces if we want t o! The rest of t his chapt er exam ines a few ways t hat we can im prove our applicat ions by doing j ust t hat .
Code Reuse One of t he basic t enet s of good program m ing is t o writ e rout ines t hat can be reused as m uch as possible. For exam ple, a generic sort ing rout ine, such as t he sim ple bubble sort shown in List ing 11- 3 , can be used t o sort an array of any sim ple dat a t ype.
List in g 1 1 - 3 . A Ge n e r ic Bu bble Sor t 'A simple, generic, slow bubble sort, to sort a 1D array Sub Generic1DBubbleSort(ByRef vaArray As Variant) Dim bDoAgain As Boolean Dim vTemp As Variant Dim iIndex As Integer Do 'Assume we're done bDoAgain = False 'Loop through the array, comparing the names For iIndex = LBound(vaArray) To UBound(vaArray) - 1 'If we found some in the wrong order, ... If vaArray(iIndex) > vaArray(iIndex + 1) Then '... swap them ... vTemp = vaArray(iIndex) vaArray(iIndex) = vaArray(iIndex + 1) vaArray(iIndex + 1) = vTemp '... and remember to loop again. bDoAgain = True End If Next Loop While bDoAgain End Sub
Unfort unat ely, we can't use t his rout ine t o sort obj ect s, because t here is not hing in t he code t o say which propert y t o sort on; every t ype of obj ect would need a specific version of t he rout ine. Let 's assum e t hat we're writ ing an applicat ion for a publishing com pany t o m anage t he product ion of a book, we're using an obj ect - orient ed design and we have a CAut hor class and a CReviewer class
( am ong ot hers) . The CAut hor class m ight look som et hing like List ing 11- 4 ( but wit h m ore propert ies t han j ust t he nam e! ) .
List in g 1 1 - 4 . A CAu t h or Cla ss 'Name: 'Description:
CAuthor Class to represent a book's author
Option Explicit Dim msAuthName As String Public Property Let AuthorName(sNew As String) msAuthName = sNew End Property Public Property Get AuthorName() As String AuthorName = msAuthName End Property
At som e point in t he applicat ion, we have t he requirem ent t o produce a list of Aut hors, sort ed by t he aut hor's nam e. Because t his is a collect ion of obj ect s we're sort ing, we cannot j ust pass t hem t o a generic rout ine; we have t o use a specific rout ine for each obj ect t ype such as t hat shown in List ing 11- 5 t o sort a collect ion of Aut hors using t he Aut horNam e propert y.
List in g 1 1 - 5 . A Bu bble Sor t for t h e CAu t h or Cla ss 'A simple bubble sort, to sort a collection of CAuthor objects Sub BubbleSortAuthors(ByRef colAuthors As Collection) Dim Dim Dim Dim
bDoAgain As Boolean iIndex As Integer clsAuthorLow As CAuthor clsAuthorHigh As CAuthor
Do 'Assume we're done bDoAgain = False 'Loop through the collection, comparing the names For iIndex = 1 To colAuthors.Count - 1 'Get the Author objects from the collection at this point Set clsAuthorLow = colAuthors(iIndex) Set clsAuthorHigh = colAuthors(iIndex + 1)
'If we found some in the wrong order, ... If clsAuthorLow.AuthorName > clsAuthorHigh.AuthorName Then '... swap them ... colAuthors.Remove iIndex + 1 colAuthors.Add clsAuthorHigh, , iIndex '... and remember to loop again. bDoAgain = True End If Next Loop While bDoAgain End Sub
Sim ilarly, we m ight need specific rout ines t o sort collect ions of CReviewer, CEdit or, CDist ribut or and so fort h obj ect s. Wouldn't it be m uch bet t er if we could have a single rout ine t hat could sort collect ions of any of t hose obj ect s? I f we use a cust om int erface, we can!
Defining a Custom Interface I f we want t o creat e a generic sort rout ine t hat will work wit h any of our classes, we need t o be able t o t alk t o each class in t he sam e wayt hat is, t hrough t he sam e int erfaceby saying t o each one " I don't care what class you are, j ust give m e som et hing t o sort you by." To achieve t hat , we need t o give each of our classes a cust om int erface, t hrough which we can ask for t he it em t o sort wit h. Our cust om int erface will be called I Sort ableObj ect ( by convent ion, int erfaces st art wit h a capit al I ) and will have a single propert y called Sort Key. The generic obj ect sort ing rout ine can t hen use t hat int erface t o ask each obj ect for it s key, wit hout caring what t ype of class it is. As m ent ioned previously, whenever we creat e a class m odule, t he VBA com piler also creat es an int erface of t he sam e nam e, cont aining all t he public propert ies, m et hods and event s t hat we add t o t he class. All we need t o do t o define a cust om int erface, t hen, is t o creat e a new class m odule t hat cont ains t he propert ies and m et hods we want t o use, but doesn't have any code in t he rout ines. VBA will creat e t he int erface for us behind t he scenes, which we can t hen add t o our ot her classes. So we can define our I Sort ableObj ect int erface by adding a new class m odule, giving it t he nam e I Sort ableObj ect and a public Sort Key propert y, as shown in List ing 11- 6.
List in g 1 1 - 6 . A I Sor t a ble Obj e ct I n t e r fa ce Cla ss 'Name: 'Description: 'Author:
ISortableObject Class to define the ISortableObject interface Stephen Bullen
'Get the key to use in the generic sorting routine Public Property Get SortKey() As Variant End Property
That 's all t here is t o it . Not e t hat we've defined t he Sort Key propert y t o ret urn a Variant dat a t ype, so we can use t he sam e generic rout ine for ot her obj ect s t hat it m ay be m ore appropriat e t o sort by a num ber or dat e.
Implementing a Custom Interface Aft er we've defined our int erface, we have t o add it t o all t he classes t hat we want t o use it wit h. We do t his by adding t he I m plem ent s keyword at t he t op of t he class m odule:
Implements IsortableObject
Figure 11- 1 shows t hat as soon as we add t hat line t o t he class, t he int erface nam e appears in t he obj ect dropdown at t he t op- left of t he code pane, j ust like an obj ect on a userform .
Figu r e 1 1 - 1 . Th e I n t e r fa ce Appe a r s in t h e Obj e ct D r op- D ow n
When t he int erface nam e is select ed in t hat drop- down, t he right - hand drop- down list s t he m et hods and propert ies defined for t hat int erface, as shown in Figure 11- 2.
Figu r e 1 1 - 2 . W it h t h e I n t e r fa ce Se le ct e d, t h e Pr ope r t ie s a n d M e t h ods Appe a r in t h e Righ t - H a n d D r op- D ow n
Clicking one of t he m et hods adds an out line procedure t o t he code m odule, j ust as it does for a userform obj ect 's event . We j ust need t o add code t o t hat rout ine t o ret urn t he value t o use when sort ing t his t ype of obj ect . The com plet e, sort able CAut hor class is shown in List ing 11- 7, where t he code t o im plem ent t he I Sort ableObj ect int erface has been highlight ed.
List in g 1 1 - 7 . Th e Sor t a ble CAu t h or Cla ss 'Name: 'Description:
CAuthor Class to represent a book's author
'Allow this type of object to be sortable 'by the generic routine Implements ISortableObject Dim msAuthName As String Public Property Let AuthorName(sNew As String) msAuthName = sNew End Property Public Property Get AuthorName() As String AuthorName = msAuthName End Property 'Return the value to be used when sorting this object Private Property Get ISortableObject_SortKey() As Variant ISortableObject_SortKey = AuthorName End Property
Not e t hat t he nam e of t he I Sort ableObj ect _Sort Key rout ine is t he concat enat ion of t he int erface nam e and t he propert y nam e and t hat it is a Privat e propert y of t he CAut hor class, so won't appear on t he CAut hor int erface.
Using a Custom Interface Wit h t he cust om I Sort ableObj ect int erface defined and im plem ent ed in our CAut hor class, we can m odify our BubbleSort Aut hors rout ine t o be able t o sort collect ions of any class t hat im plem ent s our I Sort ableObj ect int erface, shown in List ing 11- 8. All we need t o do is t o define our dat a t ypes As I Sort ableObj ect inst ead of As CAut hor, use t he Sort Key propert y inst ead of Aut horNam e and change t he variable nam es t o be m ore generic.
List in g 1 1 - 8 . A Bu bble Sor t for Cla sse s Th a t I m ple m e n t I Sor t a ble Obj e ct 'A simple bubble sort, to sort a collection of objects 'that implement ISortableObject Sub BubbleSortSortableObjects(ByRef colSortable As Collection) Dim Dim Dim Dim
bDoAgain As Boolean iIndex As Integer clsSortable1 As ISortableObject clsSortable2 As ISortableObject
Do 'Assume we're done bDoAgain = False 'Loop through the collection, comparing the names For iIndex = 1 To colSortable.Count - 1 'Get the objects from the collection at this point Set clsSortable1 = colSortable(iIndex) Set clsSortable2 = colSortable(iIndex + 1) 'If we found some in the wrong order, ... If clsSortable1.SortKey > clsSortable2.SortKey Then '... swap them ... colSortable.Remove iIndex + 1 colSortable.Add clsSortable2, , iIndex '... and remember to loop again. bDoAgain = True End If Next Loop While bDoAgain End Sub
We can t hen use t his rout ine wit h any t ype of obj ect t hat im plem ent s t he I Sort ableObj ect int erface, as shown in List ing 11- 9. This t echnique assum es t hat t he values provided by each obj ect 's I Sort ableObj ect _Sort Key propert y can be used wit hin a " great er t han" com parison.
List in g 1 1 - 9 . Usin g t h e Ge n e r ic Sor t in g Rou t in e for a Colle ct ion of CAu t h or s Sub AuthorSortExample() Dim vItem As Variant Dim colAuthors As Collection Dim clsAuthor As CAuthor Set colAuthors = New Collection 'Populate the Authors collection For Each vItem In Array("Stephen Bullen", "Rob Bovey", _ "John Green") Set clsAuthor = New CAuthor clsAuthor.AuthorName = CStr(vItem) colAuthors.Add clsAuthor Next 'Sort the Authors using the generic routine BubbleSortSortableObjects colAuthors 'Show the sorted list For Each clsAuthor In colAuthors Debug.Print clsAuthor.AuthorName Next End Sub
That was a very quick int roduct ion t o cust om int erfaces, so let 's recap what we've achieved and why we're doing it . When we creat e nont rivial obj ect m odels, we oft en end up wit h m ult iple obj ect t ypes ( t hat is, classes) t hat have a lot of propert ies in com m on, but som e significant differences. We also oft en need t o process m any of t hose obj ect t ypes in sim ilar ways ( such as sort ing t hem ) . We could do t his using a variable declared As Obj ect and hope t hat all our classes use t he sam e nam es for t heir com m on propert ies, but t hat is neit her robust nor efficient . I nst ead, we can define a cust om int erface which cont ains t he propert ies and m et hods t hat are com m on t o our obj ect s and add code t o each class t o im plem ent t he int erface. Our processes can t hen com m unicat e wit h any of t hose obj ect t ypes t hrough t he cust om int erface, m aking our code m uch m ore robust , efficient , m aint ainable and reusable.
Polymorphic Classes The abilit y of a class t o appear t o be m any different t ypes of obj ect is called poly m or ph ism and is som et hing t hat m any of t he classes in Excel's obj ect m odel use. For exam ple, we can access t he different aspect s of t he various m enu it em t ypes using t heir det ailed int erfacesCom m andBarPopUp, Com m andBarBut t on, Com m andBarCom boBox and so onor it erat e t hrough t hem all using t he m ore generic set of propert ies t hat t hey expose t hrough t he Com m andBarCont rol int erface. We can m ake our own classes polym orphic by sim ply defining and im plem ent ing m ult iple cust om int erfaces, in t he sam e way t hat we added t he I Sort ableObj ect int erface. For exam ple, anot her requirem ent for our fict ional book- publishing applicat ion m ight be t o generat e a let t er for everyone involved in t he book's product ion. I deally we would like t o be able t o put all t he CAut hor, CReviewer, CEdit or and CDist ribut or obj ect s int o a single collect ion, sort t he collect ion and t hen loop t hrough it t o generat e t he let t ers. Adding all t he obj ect s int o one collect ion is not a problem t he Collect ion obj ect can handle m ixed obj ect t ypes. Assum ing all t hose classes have im plem ent ed our I Sort ableObj ect int erface, sort ing t he collect ion is not an issue eit herour generic sort ing rout ine doesn't care what t ype of obj ect it 's looking at , so long as it im plem ent s t he int erface. The problem com es when we want t o generat e t he let t ershow do we it erat e t hrough t he collect ion of m ixed obj ect t ypes t o get t he cont act det ails? The answer, of course, is t o add anot her cust om int erface t o t hose classes, t hrough which we can access t he cont act det ails and ot her propert ies t hat are com m on t o all t he obj ect s ( assum ing we've ext ended t he earlier CAut hor class t o include t hose det ails) . We m ight choose t o call it t he I Cont act Det ails int erface, shown in List ing 11- 10, and include t he nam e, post al address and so fort h.
List in g 1 1 - 1 0 . Th e I Con t a ct D e t a ils I n t e r fa ce Cla ss 'Name: 'Description: 'Author:
IContactDetails Class to define the IContactDetails interface Stephen Bullen
'Get/set the name Public Property Get Name() As String End Property Public Property Let Name(sNew As String) End Property 'Get/set the postal address Public Property Get Address() As String End Property Public Property Let Address(sNew As String) End Property
The ext ended CAut hor, CReviewer, CEdit or and CDist ribut or classes can im plem ent t hat int erface, result ing in t he CAut hor class looking like List ing 11- 11, where t he ext ra code t o im plem ent t he I Cont act Det ails int erface has been highlight ed.
List in g 1 1 - 1 1 . Th e CAu t h or Cla ss I m ple m e n t in g t h e I Con t a ct D e t a ils I n t e r fa ce 'Name: 'Description:
CAuthor Class to represent a book's author
'Allow this type of object to be sortable 'by the generic routine Implements ISortableObject 'Provide access through the IContactDetails interface Implements IContactDetails Dim msAuthName As String Dim msAddress As String 'Set/get the Author name Public Property Let AuthorName(sNew As String) msAuthName = sNew End Property Public Property Get AuthorName() As String AuthorName = msAuthName End Property 'Set/Get the address Public Property Let Address(sNew As String) msAddress = sNew End Property Public Property Get Address() As String Address = msAddress End Property 'Implement the ISortableObject class Private Property Get ISortableObject_SortKey() As Variant ISortableObject_SortKey = AuthorName End Property 'Implement the IContactDetails interface, 'by calling through to the default interface's properties Private Property Let IContactDetails_Name(RHS As String) Me.AuthorName = RHS
End Property Private Property Get IContactDetails_Name() As String IContactDetails_Name = Me.AuthorName End Property Private Property Let IContactDetails_Address(RHS As String) Me.Address = RHS End Property Private Property Get IContactDetails_Address() As String IContactDetails_Address = Me.Address End Property
When using t he int erface and procedure nam e drop- downs t o add t he Propert y Let procedures, t he VB edit or always uses RHS as t he variable nam e for t he new propert y value ( because t hat represent s t he " right - hand side" of t he propert y assignm ent expression) . I f t he code will be doing anyt hing ot her t han j ust passing t he value on t o anot her procedure, it is a very good idea t o give t he variable a m ore m eaningful nam e, in line wit h t he best pract ices on nam ing convent ions explained in Chapt er 3 Excel and VBA Developm ent Best Pract ices. Aft er we've added t he int erface t o all our classes, we can add t he classes t o a single collect ion, sort t he collect ion using t he I Sort ableObj ect int erface and it erat e t hrough it using t he I Cont act Det ails int erface, shown in List ing 11- 12. The ShowDet ails procedure processes t he cont act det ails for each obj ect in t he collect ion, again using t he I Cont act Det ails int erface, and is explained lat er.
List in g 1 1 - 1 2 . Sor t in g a n d List in g M ix e d Cla sse s Th a t I m ple m e n t I Sor t a ble Obj e ct a n d I Con t a ct D e t a ils Sub
CombinedIterateExample()
Dim Dim Dim Dim Dim
vItem As Variant colMailList As Collection clsAuthor As CAuthor clsReviewer As CReviewer clsDetails As IContactDetails
Set colMailList = New Collection 'Add the Authors to the collection For Each vItem In Array("Stephen Bullen", "Rob Bovey", _ "John Green") Set clsAuthor = New CAuthor clsAuthor.AuthorName = CStr(vItem) colMailList.Add clsAuthor Next
'Add some Reviewers to the collection For Each vItem In Array("Dick Kusleika", _ "Beth Melton", "Shauna Kelly", "Jon Peltier") Set clsReviewer = New CReviewer clsReviewer.ReviewerName = CStr(vItem) colMailList.Add clsReviewer Next 'Sort the Mailing list using the generic routine BubbleSortSortableObjects colMailList 'Although colMailList is a collection of mixed object types, 'they all implement the IContactDetails interface, so we can 'process them all by using an object variable declared 'As IContactDetails For Each clsDetails In colMailList ShowDetails clsDetails Next End Sub
We can use t he TypeOf funct ion t o t est whet her a class im plem ent s a cert ain int erface and swit ch bet ween int erfaces by declaring a variable as t he t ype of int erface we want t o look t hrough, t hen set t ing it t o refer t o t he obj ect , as shown in List ing 11- 13. Regardless of which int erface we're looking t hrough, t he VB TypeNam e( ) funct ion will always ret urn t he obj ect 's class nam e.
List in g 1 1 - 1 3 . Ch e ck in g a n Obj e ct 's I n t e r fa ce s 'Show the details of any given object Sub ShowDetails(objUnknown As Object) 'Two variables that we can use to look at the object 'through two different interfaces Dim clsAuthor As CAuthor Dim clsDetails As IContactDetails 'Check if this object has the full CAuthor interface If TypeOf objUnknown Is CAuthor Then 'Yes, so look at the object through the CAuthor interface Set clsAuthor = objUnknown 'Write a special message for the authors Debug.Print clsAuthor.AuthorName & " wrote the book" 'Does the object implement the IContactDetails interface?
ElseIf TypeOf objUnknown Is IContactDetails Then 'Yes, so look at it through that interface Set clsDetails = objUnknown 'And write a message for everyone that helped Debug.Print clsDetails.Name & " helped with the book" Else 'An object we can't use, so write the class name Debug.Print "Unknown Object: " & TypeName(objUnknown) End If End Sub
Improving Robustness The abilit y t o it erat e t hrough a collect ion of different obj ect t ypes could be achieved wit hout using a cust om int erface, by declaring t he variable As Obj ect and ensuring t hat all t he classes we want t o access have t he sam e propert ies and m et hods. However, t hat m akes t he obj ect lat e bound, so it 's slower, doesn't show any I nt elliSense, coding errors aren't caught unt il runt im e and it relies on all t he classes using t he sam e nam es for t heir propert ies. Had we t ried t o im plem ent t he preceding funct ionalit y using a generic Obj ect t ype, we would have had a few issues t o resolve: Having st art ed wit h CAut hor.Aut horNam e and CReviewer. ReviewerNam e, we would have had t o add a com m on .Nam e propert y t o bot h, result ing in t wo propert ies t hat do t he sam e t hing. Alt ernat ively, we could have checked t he rest of t he applicat ion and changed Aut horNam e and ReviewerNam e t o Nam e wherever it was used. We would have t o expose a ll t he propert ies of t he class on it s ( single) default int erface, including t hose such as t he Sort Key propert y t hat are only used for specific " int ernal" funct ionalit y. I f we design our applicat ion t o use t he generic Obj ect t ype inst ead of cust om int erfaces t o it erat e t hrough m ixed obj ect t ypes, we rely on an im plicit agreem ent t hat our obj ect s will have t he correct propert y nam es and any errors due t o m issing, renam ed or sim ply m ist yped propert ies won't be found unt il run t im e. Taking t he ext ra st ep t o define and use a cust om int erface gives us all t he benefit s of early binding ( speed, I nt elliSense and com pile- t im e t ype checking) as well as e x plicit ly st at ing how t he classes and t heir consum ers int eract , which can only help t o im prove t he robust ness of our applicat ions.
Simplifying Development One of t he m ost used t im e- saving t ools in t he Visual Basic Edit or is t he I nt elliSense popup t hat appears aft er t yping a period ( .) aft er an obj ect . This popup list s all t he m et hods and propert ies t hat are defined in t he int erface for t hat t ype of obj ect . Unfort unat ely, when we t ry t o set propert ies or call m et hods in a worksheet or userform class, t he I nt elliSense list cont ains so m any it em s t hat it 's hard t o find t he propert ies and m et hods t hat we need t o use. I f we follow t he recom m endat ions for encapsulat ing our code, for exam ple, we shouldn't be set t ing any of a userform 's propert ies from out side t he form ; we should inst ead be exposing t he form 's funct ionalit y t hrough our own propert ies and m et hods. When viewing t he I nt elliSense popup for a userform , it shows our propert ies and m et hods m ixed in wit h t hose of t he form . Defining and using our own int erface for t he form enables us t o rest rict t he list of propert ies and m et hods t o only t hose t hat we choose t o expose.
A Progress Bar Many applicat ions include som e form of progress indicat ion t o show t he st at us of lengt hy rout ines. I t 's likely t hat such an indicat ion will be used in m ult iple places in our applicat ion and it m akes sense t o im plem ent it as a com m on funct ion t hat can be called from all our rout ines. I f we have an obj ect - orient ed design, we would ideally like t o t reat it j ust like any ot her obj ect , using som et hing like t he code in List ing 11- 14.
List in g 1 1 - 1 4 . Usin g a Pr ogr e ssBa r Cla ss Sub LongRoutine() Dim pbProgBar As ProgressBar Dim iCounter As Integer Set pbProgBar = New ProgressBar pbProgBar.Title = "Professional Excel Development" pbProgBar.Text = "Preparing report, please wait..." pbProgBar.Min = 0 pbProgBar.Max = 1000 pbProgBar.Progress = 0 pbProgBar.Show For iCounter = 0 To 1000 pbProgBar.Progress = iCounter Next pbProgBar.Hide
End Sub
There is not hing in t his code t o suggest t hat t he progress indicat ion is a userform . The code is only saying t hat we want t o display som e t ype of progress indicat ion t o t he user; t he way in which it 's present ed is ent irely encapsulat ed wit hin t he ProgressBar class and could j ust as easily be a userform , a m essage in t he st at us bar or an audible prom pt . To help ot her developers t hat m ight use t he ProgressBar class, it would be ideal if t he I nt elliSense list only showed t he seven propert ies and m et hods ( Tit le, Text , Min, Max, Progress, Show and Hide) t hat we should be using t o cont rol t he progress bar. Unfort unat ely, if t he ProgressBar was a userform class, t he I nt elliSense list would show our 7 it em s lost am ong t he ot her 57 propert ies and m et hods of userform s. As well as m aking it harder t o pick out t he correct propert ies and m et hods t o use, exposing t he norm al userform propert ies m akes it t em pt ing for t he consum er of t he progress bar class t o set som e of t he ot her propert ies of t he form . At worst , t hat could break t he way in which t he progress bar works, or m ake t he progress bar appear different ly in different part s of t he applicat ion. At best , it would m ake it m uch harder for us t o m odify t he im plem ent at ion of t he progress bar it self; our new im plem ent at ion m ay break t he nonst andard way in which t he progress bar class has been used, so we would have t o check ( and t est ) everywhere t hat it 's referred t o. By using a cust om int erface, we can gu a r a n t e e t hat all users of t he progress bar class are only able t o use t he propert ies and m et hods t hat we define in t hat int erface. Doing so rem oves t he t em pt at ion t o use t he norm al userform propert ies and m akes it im possible for consum ers of t he class t o use t he progress bar form in non- st andard ways. This enables us t o t ot ally separat e t he im ple m e n t a t ion of t he progress indicat ion from t he u se of t he progress indicat ion; as long as we keep t he sam e int erface, we can im plem ent it as a userform or as a sim ple class m odule t hat j ust updat es t he st at us bar.
The IProgressBar Interface As before, we define t he int erface t o use for our progress bar form by creat ing a new class m odule, giving it t he nam e I ProgressBar and adding em pt y rout ines for each of t he elem ent s on t he int erface, as shown in List ing 11- 15.
List in g 1 1 - 1 5 . Th e I Pr ogr e ssBa r I n t e r fa ce Cla ss 'Set and get the title Public Property Let Title(sNew As String) End Property Public Property Get Title() As String End Property 'Set and get the descriptive text
Public Property Let Text(sNew As String) End Property Public Property Get Text() As String End Property 'Set and get the minimum value for the bar Public Property Let Min(dNew As Double) End Property Public Property Get Min() As Double End Property 'Set and get the maximum value for the bar Public Property Let Max(dNew As Double) End Property Public Property Get Max() As Double End Property 'Set and get the progress point Public Property Let Progress(dNew As Double) End Property Public Property Get Progress() As Double End Property 'Show the progress bar Public Sub Show() End Sub 'Hide the progress bar Public Sub Hide() End Sub
The FProgressBar Form The FProgressBar form im plem ent s t he I ProgressBar int erface by displaying t he progress indicat ion on a userform . The progress bar is m ade up of t wo superim posed fram es, each cont aining a label. The back fram e and label is blue- on- whit e, and t he front fram e and label is whit e- on- blue. The progress m easure cont rols t he widt h of t he front fram e, t o give t he appearance of t he progress bar shown in Figure 11- 3.
Figu r e 1 1 - 3 . A Sim ple Pr ogr e ss Ba r For m
The com plet e FProgressBar form can be found on t he CD, in t he workbook \ Concept s\ Ch11I nt erfaces\ Progress Bars.xls, but is reproduced in a sim ple form in List ing 11- 16.
List in g 1 1 - 1 6 . Th e FPr ogr e ssBa r For m M odu le I m ple m e n t in g t h e I Pr ogr e ssBa r I n t e r fa ce ' ' Name: ' Description: ' Author:
FProgressBar Displays a modeless progress bar on the screen Stephen Bullen
Option Explicit ' Implement the IProgressBar interface Implements IProgressBar ' Store the Min, Max and Progress values in module variables Dim mdMin As Double Dim mdMax As Double Dim mdProgress As Double Dim mdLastPerc As Double ' Initialize the form to show blank text Private Sub UserForm_Initialize() lblMessage.Caption = "" Me.Caption = "" End Sub 'Ignore clicking the [x] on the dialog Private Sub UserForm_QueryClose(Cancel As Integer, _ CloseMode As Integer) If CloseMode = vbFormControlMenu Then Cancel = True End Sub ' Let the calling routine set/get the caption of the form Private Property Let IProgressBar_Title(RHS As String) Me.Caption = RHS End Property
Private Property Get IProgressBar_Title() As String IProgressBar_Title = Me.Caption End Property ' Let the calling routine set/get the descriptive text Private Property Let IProgressBar_Text(RHS As String) If RHS lblMessage.Caption Then lblMessage.Caption = RHS End If End Property Private Property Get IProgressBar_Text() As String IProgressBar_Text = lblMessage.Caption End Property ' Let the calling routine set/get the Minimum scale Private Property Let IProgressBar_Min(RHS As Double) mdMin = RHS End Property Private Property Get IProgressBar_Min() As Double IProgressBar_Min = mdMin End Property ' Let the calling routine set the Maximum scale Private Property Let IProgressBar_Max(RHS As Double) mdMax = RHS End Property Private Property Get IProgressBar_Max() As Double IProgressBar_Max = mdMax End Property
' Let the calling routine set the progress amount. ' Update the form to show the progress. Private Property Let IProgressBar_Progress(RHS As Double) Dim dPerc As Double mdProgress = RHS 'Calculate the progress percentage If mdMax = mdMin Then dPerc = 0 Else dPerc = Abs((RHS - mdMin) / (mdMax - mdMin)) End If 'Only update the form every 0.5% change
If Abs(dPerc - mdLastPerc) > 0.005 Then mdLastPerc = dPerc 'Set the width of the inside frame, 'rounding to the pixel fraInside.Width = Int(lblBack.Width * dPerc / _ 0.75 + 1) * 0.75 'Set the captions for the blue-on-white and 'white-on-blue text lblBack.Caption = Format(dPerc, "0%") lblFront.Caption = Format(dPerc, "0%") 'Refresh the form if it's being shown If Me.Visible Then Me.Repaint End If End If End Property Private Property Get IProgressBar_Progress() As Double IProgressBar_Progress = mdProgress End Property
'Show the form modelessly Private Sub IProgressBar_Show() Me.Show vbModeless End Sub 'Hide the form Private Sub IProgressBar_Hide() Me.Hide End Sub
The only differences bet ween t his code and t he " plain" Progress Bar form we saw in Chapt er 10 Userform Design and Best Pract ices are t hat t he Tit le, Text , Min, Max and Progress propert ies have been exposed via t he I ProgressBar int erface and we've added our own Show and Hide m et hods t o show and hide t he form using t hat int erface. The difference bet ween t he I nt elliSense displays when using our cust om I ProgressBar int erface inst ead of t he form 's default int erface can be seen in Figure 11- 4 and Figure 11- 5. By im plem ent ing t he int erface, t he consum er of our progress bar form has a m uch clearer display of t he propert ies and m et hods t hat should be used t o cont rol t he progress bar. Figure 11- 4 shows t he I nt elliSense popup we get if we add t he progress bar propert ies direct ly t o t he form , and Figure 11- 5 shows t he m uch sim pler I nt elliSense list we get when using t he cust om int erface.
Figu r e 1 1 - 4 . Usin g t h e For m 's D e fa u lt I n t e r fa ce Sh ow s All t h e Use r for m 's
Pr ope r t ie s in t h e I n t e lliSe n se List , Obscu r in g t h e On e s for t h e Pr ogr e ss Ba r I t se lf
Figu r e 1 1 - 5 . Usin g t h e Cu st om I Pr ogr e ssBa r I n t e r fa ce Lim it s t h e I n t e lli Se n se List t o On ly t h e I t e m s W e W a n t t o Ex pose , Sim plifyin g t h e Use of t h e For m
The CProgressBar Class Aft er we know t hat t he consum er of our progress bar is accessing it t hrough our cust om int erface, we are free t o m odify t he im plem ent at ion of t he progress indicat ion in any way we like. As long as we keep t he int erface t he sam e, we k n ow t he code t hat uses t he class will cont inue t o work. The opposit e is also t rue; as consum ers of t he class, we k n ow as long as t he int erface is kept t he sam e, t he creat or of t he class cannot change t he nam e of any of t he propert ies or m et hods and in doing so break our code. By way of exam ple, t he code in List ing 11- 17 im plem ent s t he int erface using a class m odule inst ead of a userform and displays t he progress on Excel's st at us bar.
List in g 1 1 - 1 7 . Th e CPr ogr e ssBa r Cla ss I m ple m e n t in g t h e I Pr ogr e ssBa r I n t e r fa ce ' ' ' '
Class to show a progress indication in the status bar. Implements to IProgressBar interface to allow easy switching between showing the progress on the status bar (this class) or on a userform (the FProgressBar form).
Option Explicit 'Implement the IProgressBar interface
Implements IProgressBar 'Module-level variables to store the property values Dim msTitle As String Dim msText As String Dim mdMin As Double Dim mdMax As Double Dim mdProgress As Double Dim mbShowing As Boolean Dim msLastCaption As String
'Assume Private mdMin mdMax End Sub
an initial progress of 0-100 Sub Class_Initialize() = 0 = 100
'Set and get the title Private Property Let IProgressBar_Title(RHS As String) msTitle = RHS If mbShowing Then UpdateStatusBar End Property Private Property Get IProgressBar_Title() As String IProgressBar_Title = msTitle End Property
'Set and get the descriptive text Private Property Let IProgressBar_Text(RHS As String) msText = RHS If mbShowing Then UpdateStatusBar End Property Private Property Get IProgressBar_Text() As String IProgressBar_Text = msText End Property
'Set and get the minimum value for the bar Private Property Let IProgressBar_Min(RHS As Double) mdMin = RHS If mbShowing Then UpdateStatusBar End Property Private Property Get IProgressBar_Min() As Double IProgressBar_Min = mdMin End Property
'Set and get the maximum value for the bar Private Property Let IProgressBar_Max(RHS As Double) mdMax = RHS If mbShowing Then UpdateStatusBar End Property Private Property Get IProgressBar_Max() As Double IProgressBar_Max = mdMax End Property
'Set and get the progress point Private Property Let IProgressBar_Progress(RHS As Double) mdProgress = RHS If mbShowing Then UpdateStatusBar End Property Private Property Get IProgressBar_Progress() As Double IProgressBar_Progress = msprogress End Property
'Show the progress bar Private Sub IProgressBar_Show() mbShowing = True mdLastProgress = 0 UpdateStatusBar End Sub
'Hide the progress bar Private Sub IProgressBar_Hide() Application.StatusBar = False mbShowing = False End Sub
'Private routine to show the progress indication 'on the status bar Private Sub UpdateStatusBar() Dim dPerc As Double Dim sCaption As String 'Calculate the progress percentage If mdMax = mdMin Then dPerc = 0 Else dPerc = Abs((mdProgress - mdMin) / (mdMax - mdMin)) End If 'Create the caption
If Len(msTitle) > 0 Then sCaption = msTitle If Len(msTitle) > 0 And Len(msText) > 0 Then sCaption = sCaption & ": " End If If Len(msText) > 0 Then sCaption = sCaption & msText 'Calculate and add the formatted percentage sCaption = sCaption & " (" & Format$(dPerc, "0%") & ")" 'Update the status bar if it's changed If sCaption msLastCaption Then msLastCaption = sCaption Application.StatusBar = sCaption End If End Sub
The calling code can very easily swit ch bet ween using eit her t he form or t he st at us bar for t he progress display ( perhaps according t o a user's preference) , as shown in List ing 11- 18.
List in g 1 1 - 1 8 . Usin g t h e I Pr ogr e ssBa r I n t e r fa ce Allow s t h e Ch oice Be t w e e n t h e For m a n d t h e Cla ss Sub LongRoutine(bProgressInForm As Boolean) 'Always use the IProgressBar interface Dim pbProgBar As IProgressBar Dim iCounter As Integer If bProgressInForm Then 'Use the progress bar form Set pbProgBar = New FProgressBar Else 'Use the status bar class Set pbProgBar = New CProgressBar End If 'The rest of the code is unchanged pbProgBar.Title = "Professional Excel Development" pbProgBar.Text = "Preparing report, please wait..." pbProgBar.Min = 0 pbProgBar.Max = 1000 pbProgBar.Progress = 0 pbProgBar.Show
For iCounter = 0 To 1000 pbProgBar.Progress = iCounter Next pbProgBar.Hide End Sub
A Plug-in Architecture You saw in Chapt er 10 Userform Design and Best Pract ices how it was possible t o creat e a user int erface consist ing of m odeless userform s, in which t he int eract ion wit h t he user occurs wit hin userform s ( as opposed t o worksheet s) , yet wit h t he com m and bars st ill usable. To allow t he form s t o respond t o m enu bar clicks ( such as saving, m oving t o anot her form or closing t he applicat ion) , we had t o ensure t hat all our form s had t he sam e basic set of rout ines, t hat could be called by our com m on m enu handler. Those rout ines were called BeforeNavigat e, BeforeSave, Aft erSave and AppExit . We had in fact creat ed our own im plicit int erface, wit hout knowing it . By m aking t hat int erface explicit , we can im prove robust ness and reliabilit y and sim plify t he developm ent of t he applicat ion. We'll call t his int erface I PlugI nForm and define it as shown in List ing 11- 19, where we've also added a Show m et hod, t o be able t o show t he form t hrough t his int erface.
List in g 1 1 - 1 9 . Th e I Plu gI n For m I n t e r fa ce Cla ss 'Name: 'Description: 'Author:
IPlugInForm Interface to be implemented by each form Stephen Bullen
'The form's name Public Property Get Name() As String End Property 'Show the form Public Sub Show(Optional ByVal Style As _ FormShowConstants = vbModal) End Sub 'The user clicked a menu item to navigate to a different form 'Save any changes on the form and unload Public Sub BeforeNavigate(ByRef bCancel As Boolean) End Sub 'The user clicked the Save button 'Save any changes on the form and unload Public Sub BeforeSave(ByVal bSaveAs As Boolean, _ ByRef bCancel As Boolean) End Sub 'After the save completed 'Update the form with any new information Public Sub AfterSave(ByVal bSaveAs As Boolean) End Sub
'The user clicked the Close button to exit the application 'Tidy up and unload the form Public Sub AppExit() End Sub
I f all of our form s im plem ent t his int erface, t he cent ral cont rol rout ine shown in List ing 10- 26 in Chapt er 10 Userform Design and Best Pract ices can declare t he gfrm Act iveForm variable As I PlugI nForm inst ead of As Obj ect and call t he sam e m et hods as before. Using t he int erface enables us t o be explicit about what t he code is doing, prevent s t yping errors, ensures none of our com m on rout ines are " accident ally" delet ed from t he form s and helps enforce a com m on st ruct ure t hroughout t he applicat ion.
Practical Example The PETRAS applicat ion files for t his chapt er can be found on t he CD in t he folder \ Applicat ion\ Ch11I nt erfaces and include t he following files: Pe t r a sTe m pla t e .x lt The t im esheet t em plat e Pe t r a sAddin .x la The t im esheet dat a- ent ry support add- in Pe t r a sRe por t in g.x la The m ain report ing applicat ion Pe t r a sCon solida t ion .x lt A t em plat e t o use for new result s workbooks D e bu g.in i A dum m y file t hat t ells t he applicat ion t o run in debug m ode Pe t r a sI con .ico An icon file, t o use for Excel's m ain window
PETRAS Timesheet The PETRAS t im esheet add- in has not been updat ed for t his chapt er.
PETRAS Reporting At t his st age in t he developm ent of t he applicat ion, it would be art ificial t o add a suit e of userform s, j ust so we could dem onst rat e t he im plem ent at ion of a plug- in archit ect ure. However, such a suit e of form s will be added t o t he applicat ion in Chapt er 13 Program m ing wit h Dat abases for m aint enance of t he st at ic list s of Consult ant s, Client s, Proj ect s and so fort h. For t his chapt er, we will m odify t he progress bar handling t o display t he consolidat ion progress unobt rusively in t he st at us bar if we're consolidat ing fewer t han t en t im esheet workbooks, but pop up a cancelable progress bar userform if consolidat ing t en or m ore t im esheet s. As such, we'll be including t he I ProgressBar int erface from List ing 11- 15, t he FProgressBar form from List ing 11- 16 and t he CProgressBar class from List ing 11- 17. I n t his exam ple, t he form has an ext ra Cancel but t on and t he int erface has been enhanced t o add a Cancelled propert y, set t o True when t he Cancel but t on is clicked. The code changes required for t his enhancem ent are det ailed in Table 111.
Ta ble 1 1 - 1 . Ch a n ge s t o t h e PETRAS Re por t in g Applica t ion for Ch a pt e r 1 1
M odu le
Pr oce du r e
Ch a n g e
I ProgressBar ( new class)
Added class t o define t he I ProgressBar int erface, copied from List ing 11- 15, adding Cancelable propert y.
CProgressBar ( new class)
Added class t o show t he progress in t he st at us bar, copied from List ing 11- 17.
FPr ogr essBar
Moved various m et hods t o be exposed t hrough t he I ProgressBar int erface inst ead of t he default int erface. The result ing code is sim ilar t o List ing 11- 16.
MSyst em Code
Consolidat eWor k book s Modified t o use t he I ProgressBar int erface and t est whet her t o use t he CProgressBar class or FProgressBar form .
Conclusion Whenever we creat e a class m odule in VBA, t he com piler creat es bot h t he class and a default int erface for it . The code in t he class defines how t he obj ect behaves, and t he int erface defines how we access t he code. Wit h a sm all am ount of effort , we can define our own cust om int erfaces and im plem ent t hem in our classes, enabling us t o t reat different classes as if t hey were t he sam e t ype of obj ect . When developing userform s, we can use a cust om int erface t o expose only t he propert ies and m et hods t hat apply t o t he funct ionalit y we're providing, wit hout clut t ering t he I nt elliSense list wit h all t he basic userform 's propert ies. By doing t his, we can m ake our code m ore generic, m ore robust , m ore reliable, easier t o writ e and easier t o m aint ain. By im plem ent ing a st andard cust om int erface in all our form s, report s and processes, we can design an applicat ion archit ect ure t hat is t ot ally ext ensible, wit hout requiring any changes t o t he core applicat ion. I f working in a m ult ideveloper t eam , t his int erface can be ext ended across workbooks, allowing each developer t o work independent ly on t he applicat ion's funct ions, safe in t he knowledge t hat his work will not direct ly im pede any of t he ot her developers.
Chapter 12. VBA Error Handling Error handling is one of t he m ost com m only om it t ed feat ures in Excel applicat ions. This is not an accept able st at e of affairs. The last t hing you want your users t o see is an unvarnished Excel or VBA runt im e error. They will m ost surely not underst and what t hey are seeing and t hey will oft en panic, lose fait h in your applicat ion, or bot h. A good error handling syst em will not prevent errors from occurring, but it will m ake t he process m uch less dist ressing for your users and m uch easier for you t o diagnose and correct . All t he errors discussed in t his chapt er are runt im e errors. These are errors t hat occur while your code is execut ing. The ot her t ype of error, t he com pile- t im e error, should not be a fact or at t his point . A good developer will have ensured his proj ect cleanly passes a Debug > Com pile in t he VBE before at t em pt ing t o execut e it .
Error-Handling Concepts Unhandled vs. Handled Errors Runt im e errors fall int o t wo broad cat egories: u n h a n dle d e r r or s and h a n dle d e r r or s. Sim ply put , an unhandled error is one t hat is not caught by an error handling m echanism in t he procedure where it occurs, whereas a handled error is caught by such an error handler. This is not t o im ply t hat all unhandled errors are bad. I n som e sit uat ions, you can reasonably choose not t o handle errors in a cert ain procedure, inst ead deferring t hem t o an error handler furt her up t he call st ack. The error is convert ed from an unhandled error int o a handled error at t he point where it reaches an error handling m echanism . What is unaccept able is an error t hat rem ains unhandled all t he way unt il it reaches t he user. Figure 12- 1 shows t he result of an unhandled error.
Figu r e 1 2 - 1 . An Un h a n dle d Er r or M e ssa ge
The Err Object When any kind of runt im e error occurs, t he affect ed code is said t o be in error m ode. An int rinsic, global VBA obj ect called t he Err obj ect is populat ed wit h inform at ion about t he error. Alm ost every error handling m echanism m akes use of t he Err obj ect , so it is helpful t o underst and it s m ost com m only used propert ies and m et hods. Err.Clear This m et hod clears all t he propert ies of t he Err obj ect , canceling t he current error.
Err.Description This propert y cont ains a short st ring t hat describes t he error. Err.HelpFile This propert y cont ains t he full pat h and filenam e of t he help file cont aining a descript ion of t he error. Err.HelpContext This propert y cont ains t he help cont ext I D wit hin t he help file of t he t opic t hat describes t he error. Err.LastDLLError Theoret ically, t his propert y ret urns any error code generat ed by calls t o a DLL, such as a Windows API call. I n pract ice, t his value is very unreliable because Windows m ay execut e DLL funct ions aut om at ically t hat overwrit e t he inform at ion in t his propert y before your code get s a chance t o look at it . I t 's best never t o rely on t he LastDLLError propert y. Err.Number This propert y ret urns t he num ber associat ed wit h t he m ost recent runt im e error. When your error handler needs t o t ake different act ions based on t he t ype of error t hat occurred, you should use t his propert y t o dist inguish am ong different t ypes of errors. Err.Raise This m et hod enables you t o int ent ionally raise errors wit hin your applicat ion. We discuss t his t opic in det ail lat er in t he chapt er. Err.Source This propert y ident ifies t he source of t he error. I t is not very useful for providing inform at ion about VBA runt im e errors because it sim ply ret urns t he nam e of t he proj ect in which t he error occurred. However, as we discuss lat er in t his chapt er, you can populat e t his propert y yourself wit h m ore det ailed inform at ion when raising cust om errors.
N OTE All cont ent s of t he Err obj ect are cleared aut om at ically when code execut ion encount ers a Resum e st at em ent , an On Error st at em ent , Exit Sub, Exit Funct ion, Exit Propert y, End Sub, End Funct ion, or End Propert y.
What Is an Error Handler An error handler is a labeled sect ion of a procedure t hat you designat e as t he place where code will resum e execut ing whenever a runt im e error occurs. An On Error GoTo st at em ent , discussed in t he next sect ion, is used t o m ake t his designat ion. The error handler m ust be a separat e block of code wit hin t he procedure, wit h t he only way int o it being an error and t he only way out being a Resum e, Exit Sub, or Exit Funct ion. List ing 12- 1 shows an exam ple of a procedure wit h a very sim ple error handler.
List in g 1 2 - 1 . A Pr oce du r e w it h a Sim ple Er r or H a n dle r Public Sub MyProcedure() On Error GoTo ErrorHandler
' Lots of code here. Exit Sub ErrorHandler: MsgBox Err.Description, vbCritical, "Error!" End Sub
I n t his procedure, t he sect ion of code ident ified by t he ErrorHandler label has been designat ed as t he error handler for t he procedure by t he On Error GoTo ErrorHandler st at em ent . Not e t he Exit Sub st at em ent prior t o t he ErrorHandler sect ion. This prevent s t he procedure from execut ing t he code in t he ErrorHandler sect ion if no error has occurred. Designat ing a sect ion of code as an error handler e n a b le s it . When a runt im e error occurs and code execut ion branches t o t he error handler, it is said t o be a ct ive . This difference is not academ ic. I f an error handler is act ive ( current ly in t he process of handling an error) and anot her error occurs as a result of som et hing t he code in t hat error handler does, t he new error cannot be handled by t he sam e error handler. I f t his occurs, cont rol will be passed t o t he error handler of t he next highest procedure in t he call st ack. Why is t his im port ant ? There are som e circum st ances in which you need t o perform an operat ion t hat m ay generat e anot her error inside an error handler. I f t his is t he case, put t his code int o a separat e procedure wit h it s own error handler and call t hat procedure from t he act ive error handler. Mult iple error handlers can be act ive at t he sam e t im e, so an error t hat occurs in and is handled by t his separat e procedure will not affect t he error handler t hat called it .
N OTE There is only a single global Err obj ect . I t s propert ies are set by t he error t hat occurred m ost recent ly. I f you t hink you will run int o an error wit hin an error sit uat ion, as described above, save any inform at ion about t he original error in your own variables so you don't lose it .
Error Handler Scope The scope of an error handler, which is t he body of code t hat will act ivat e it if a runt im e error occurs, includes t he procedure t hat t he error handler is defined in as well as any called procedures t hat do not have t heir own error handlers. I f a calling procedure has a designat ed error handler but t he procedures it calls do not , t he error handler of t he calling procedure will be act ivat ed by any errors t hat occur. The sim ple code exam ple in List ing 12- 2 will illust rat e t his m ore clearly.
List in g 1 2 - 2 . Th e Scope of a n Er r or H a n dle r
Public Sub EntryPointProcedure() On Error GoTo ErrorHandler SubProcedure1 Exit Sub ErrorHandler: MsgBox Err.Description, vbCritical, "Error!" End Sub Private Sub SubProcedure1() SubProcedure2 End Sub Private Sub SubProcedure2() Dim lTest As Long ' This error will activate the error handler ' in the entry point procedure. lTest = 1 / 0 End Sub
I n t his exam ple, only Ent ryPoint Procedure has defined an error handler. Ent ryPoint Procedure calls SubProcedure1 and SubProcedure1 calls SubProcedure2. I f a runt im e error occurs in a n y of t hese t hree procedures, code execut ion will im m ediat ely branch t o t he error handler defined in Ent r y Point Pr ocedur e. There are som e cases where t his is a valid and reasonable error handling t echnique, but usually it 's not t he best choice. First , you lose any inform at ion about where t he error act ually occurred. Second, if any of t he called procedures need t o perform cleanup prior t o exit ing ( dest roy obj ect , close connect ions, reset public variables and so on) , t his cleanup code will be skipped. Code execut ion branches uncondit ionally t o t he t op- level procedure's error handler and it cannot be m ade t o ret urn t o t he procedure where t he error occurred ( even wit h t he use of t he Resum e st at em ent , which we will discuss lat er in t he chapt er) . For t hese reasons, it is usually best if each procedure in your applicat ion handles it s own errors.
The On Error Statement The t hree variant s of t he On Error st at em ent provide t he foundat ion of VBA's error handling capabilit y.
On Error GoTo This st at em ent is used t o specify an error handler for a procedure. Lit erally, what it t ells VBA t o do
is branch code execut ion t o t he line in t he procedure ident ified by < Label> when a runt im e error occurs. The code below t his line is considered t he error handler for t he procedure.
On Error Resume Next This st at em ent is bot h very dangerous and very useful at t he sam e t im e. I t t ells VBA t o ignore any errors t hat occur and cont inue wit h t he next line of code unt il you t ell it t o do ot herwise. First let 's m ake it very clear what you should not do wit h t his st at em ent . All t oo oft en I receive code from a client where t he first line in several very large procedures is On Error Resume Next. Don't do t his! On Error Resume Next is not a subst it ut e for writ ing code correct ly in t he first place. I f you have a large procedure t hat will not run unless you place On Error Resume Next at t he t op of it , t hen t hat procedure is alm ost cert ainly poorly writ t en. Wit h t hat out of t he way, let 's t alk about t he circum st ances in which On Error Resume Next is useful and necessary. You will som et im es encount er sit uat ions where you expect an error t o occur during norm al program execut ion at least som e of t he t im e. I n cases such as t his you do not want t he error t o act ivat e your error handler. I nst ead you want code execut ion t o cont inue in som e condit ional fashion based on whet her or not an error occurred. For exam ple, assum e a procedure needs t o use an exist ing workbook t hat m ay or m ay not be open when t he procedure is execut ed. I n t his case, you would use On Error Resume Next t o t em porarily bypass error handling while you t est t o det erm ine whet her t he workbook is open. The m om ent you finish t his t est you would re- enable t he error handler using t he On Error Goto st at em ent . List ing 12- 3 shows an exam ple of t his.
List in g 1 2 - 3 . W h e n t o Use On Er r or Re su m e N e x t Public Sub OnErrorResumeNextDemo() Dim wkbCalcs As Workbook On Error GoTo ErrorHandler ' Lots of code here. ' Test if the Calcs.xls workbook is open. Set wkbCalcs = Nothing On Error Resume Next Set wkbCalcs = Application.Workbooks("Calcs.xls") On Error GoTo ErrorHandler ' If the workbook wasn't open we need to open it. If wkbCalcs Is Nothing Then Set wkbCalcs = Application.Workbooks.Open( _ ThisWorkbook.Path & "\Calcs.xls") End If ' Lots more code here.
Exit Sub ErrorHandler: MsgBox Err.Description, vbCritical, "Error!" End Sub
Not ice t hat On Error Resume Next is used t o disable error handling for j ust t he single line of code t hat det erm ines if t he Calcs.xls workbook is already open. This should be t he norm . Always keep t he num ber of lines of code affect ed by On Error Resume Next t o an absolut e m inim um . I f you do not t urn it off im m ediat ely when you no longer need it , you will very likely suppress errors you did not int end t o suppress. As wit h alm ost all rules, t here are a few except ions t o t he ban on ent ire procedures being " wrapped" in On Error Resume Next. The first sit uat ion concerns a special t ype of procedure in which an error is an int egral part of t he logic of t he procedure. I n t he code in List ing 12- 3, for exam ple, we could subst it ut e t he in- place t est for t he Calcs.xls workbook being open wit h a general- purpose funct ion t hat could be used anywhere t his t ype of t est was required. The result would look like t he funct ion in List ing 12- 4, which is wrapped ent irely in On Error Resume Next by design.
List in g 1 2 - 4 . An En t ir e Fu n ct ion W r a ppe d in On Er r or Re su m e N e x t Private Function bIsBookOpen(ByVal sBookName As String, _ ByRef wkbBook As Workbook) As Boolean ' Checks to see if the specified workbook is open. If it is, ' a reference to it is returned in the wkbBook argument. On Error Resume Next Set wkbBook = Application.Workbooks(sBookName) bIsBookOpen = (Len(wkbBook.Name) > 0) End Function
The second sit uat ion t hat requires wrapping an ent ire procedure in On Error Resume Next involves applicat ion shut down code. When your applicat ion is closing, you t ypically at t em pt t o perform som e cleanup. I f an error occurs during t his process, t here really isn't anyt hing useful an error handler can accom plish. I t 's t ypically bet t er t o use On Error Resume Next t o bypass any errors and cont inue perform ing what ever cleanup t he applicat ion can accom plish before it closes. You can see an exam ple of t his in t he shut down code for our sam ple add- in. The t hird sit uat ion t hat requires wrapping an ent ire procedure in On Error Resume Next involves
applicat ion shut down code and class Term inat e event s. When your applicat ion or class is going away, t here's not m uch point in act ivat ing an error handler. The best choice for when an error occurs in t hese t ypes of procedure is usually t o skip t he line t hat caused t he error and cont inue t o execut e as m uch of t he code as possible. We cover t hese cases lat er in t he chapt er.
On Error GoTo 0 This st at em ent disables any previously enabled error handler in t he current procedure. I t has no effect in procedures t hat do not cont ain error handling, even if t hey have been called by a higherlevel procedure t hat does cont ain error handling. I n List ing 12- 2, for exam ple, placing On Error GoTo 0 in SubProcedure1 would not prevent errors t hat occurred in t hat procedure from being handled by t he st ill enabled error handler in Ent ryPoint Procedure.
The Resume Statement The Resume st at em ent is used t o deact ivat e an error handler and cause code execut ion t o resum e at a specific locat ion t hat depends on which variat ion of t he st at em ent is used. The Resume st at em ent can only be used inside an act ive error handler. Using it under any ot her circum st ances will cause a runt im e error t o occur. No variet y of t he Resume st at em ent can cause code execut ion t o resum e in any procedure ot her t han t he one where t he current error handler is locat ed. This m eans if t he current error handler has t rapped an error from a lower- level procedure, Resume cannot cause code execut ion t o ret urn t o t hat procedure. You m ust be very careful wit h t he Resume st at em ent because you can very easily creat e an infinit e loop in your code wit h it . There are t hree variat ions of t he Resume st at em ent .
Resume This is t he m ost dangerous Resume st at em ent of t hem all. I t causes code execut ion t o ret urn t o t he line of code t hat caused t he error ( or t he call t o a subprocedure where t he error originat ed if t he error did not originat e in t he current procedure) . The im plicit assum pt ion is t hat your error handler has done som et hing t o correct t he error condit ion. I f t his is not t he case, t he error will j ust occur again, t riggering t he error handler, which resum es execut ion on t he line of code t hat caused t he error and so on. This is t he dreaded infinit e loop condit ion and in m any sit uat ions t he only way t o st op it is t o use Ct rl+ Alt + Del t o shut down Excel. Wit h t his warning very clear, t hough, t he Resume st at em ent can be quit e useful. I f you are at t em pt ing t o m ake a connect ion t o a rem ot e dat abase over a slow or congest ed net work, for exam ple, it is not uncom m on t o fail one or m ore t im es. When a connect ion failure occurs, an error is t hrown and your error handler is act ivat ed. You can increm ent a count er in your error handler and use Resume t o t ry connect ing again. I f you are unable t o connect successfully aft er a cert ain num ber of at t em pt s, you can have your error handler bail out wit h an error m essage t o t he user. We dem onst rat e t his use of resum e in Chapt er 13 Program m ing wit h Dat abases. The Resume st at em ent is also very useful wit hin t he cont ext of built - in debugging aides. When your code has a special flag set t hat indicat es it is in debug m ode, your error handler can direct code
execut ion t o a branch t hat aut om at ically places t he code int o break m ode and allows you t o resum e code execut ion on t he line of code t hat generat ed t he error in order t o debug t he problem . You will see t he error handling const ruct s t hat assist debug m ode in t his chapt er, but we do not cover debugging in det ail unt il Chapt er 16 VBA Debugging.
Resume Next The Resume Next st at em ent causes code execut ion t o cont inue on t he first execut able line of code aft er t he one t hat generat ed t he error. The Resume Next st at em ent will not ret urn t o a lower- level procedure if t hat 's where t he error was generat ed. I nst ead, it will resum e execut ion in t he procedure t hat handled t he error on t he line of code im m ediat ely following t he call t o t he procedure branch where t he error was generat ed.
Resume The Resume st at em ent causes code execut ion t o cont inue on t he line of code following t he specified label. The label m ust be locat ed in t he sam e procedure as t he error handler. Like t he Resume st at em ent , t he Resume st at em ent can cause an infinit e loop in your code if t he error is locat ed below t he specified label.
Raising Custom Errors Alt hough it m ay seem count erint uit ive, deliberat ely generat ing runt im e errors in your code can be a very useful t echnique and it is fully support ed by VBA. The reasons for using t hese cust om errors are bet t er dealt wit h in t he cont ext of procedure error handling as a whole, so we defer a det ailed discussion of t his t opic unt il lat er in t he chapt er. I n t his sect ion we cover t he m echanics of raising cust om errors. Cust om errors are raised using t he Raise m et hod of t he Err obj ect . The synt ax of t his m et hod is as follow s:
Err.Raise Number, Source, Description, HelpFile, HelpContextID
The argum ent s t o t he Err.Raise m et hod correspond t o t he propert ies of t he Err obj ect , which we described above. When you raise a cust om error, you can set t hese argum ent s however you like. All of t hem except t he Num ber argum ent are opt ional. One caveat is you cannot use an error num ber for a cust om error t hat is already used by an Excel or VBA error. ( You can raise predefined errors using t heir error num bers.) The num bers 513 t hrough 65535 are reserved for cust om errors. VBA also provides t he special const ant vbObj ect Error for creat ing cust om error num bers t hat are t ypically used wit h classes. Any num ber added t o t he vbObj ect Error const ant is guarant eed t o be an error num ber t hat is not used by any Windows process. An exam ple of a cust om error num ber creat ed using t he vbObj ect Error const ant is shown here:
Err.Raise vbObjectError + 1024
The Source argum ent of t he error should be set t o t he nam e of t he procedure in which t he error was raised. The Descript ion of a cust om error should be a reasonably brief but clear descript ion of t he reason t he error was raised. The HelpFile and HelpCont ext I D argum ent s enable you t o provide t he user wit h addit ional inform at ion about t he error if your proj ect uses a help file. Ot herwise, t hese argum ent s can be ignored. Help files are covered in m ore det ail in Chapt er 24 Providing Help, Securing, Packaging and Dist ribut ing.
The Single Exit Point Principle One of t he fundam ent al good archit ect ural pract ices in any procedure is t o have a single exit point . This m eans t hat aft er your error handler has finished handling an error, it should redirect code execut ion back int o t he body of t he procedure so t he procedure will be exit ed at t he sam e point under all circum st ances. The pract ical reason for t his is t hat it 's very com m on for som e t ype of cleanup t o be required before a procedure exit s. Even if a procedure current ly requires no cleanup, t his m ay very well change as a result of som e fut ure code m odificat ion. I f your error handlers are already st ruct ured t o use a single exit point , you have one less m odificat ion t o m ake. The m echanism for im plem ent ing a single exit point is t he Resum e < Label> st at em ent discussed previously. I n t his case < Label> ident ifies t he point in each procedure at which code execut ion resum es aft er an error has occurred and been handled. We will give t his label t he nam e ErrorExit . The ErrorExit label has no effect on code execut ion when t he procedure com plet es wit hout error. Norm al code execut ion j ust passes t his label and cont inues on t o t he end of t he procedure. When an error occurs, however, t his label ident ifies t he point at which t he error handler should resum e code execut ion once an error has been handled in order t o guarant ee t hat code execut ion com plet es at t he sam e point in t he procedure wit h or wit hout an error. You will see ex am ples of single exit point procedures in t he sect ions t hat follow.
Simple Error Handling I n t he sim plest form of error handling, error handlers are placed only in e n t r y poin t pr oce du r e s. Ent ry point procedures are t hose procedures from which code execut ion can be init iat ed. They are t ypically t he procedures assigned t o m enu it em s, t oolbar but t ons, or cont rols placed on worksheet s, but m ost event procedures are also ent ry point s, because t hey init iat e execut ion based on som e act ion m ade by t he user. I f t he error handler in an ent ry point procedure is t he only error handler in it s call st ack, t his error handler will t rap all errors t hat occur in all lower- level procedures. A sim ple error handler will display an error m essage t o t he user and exit t he procedure. An exam ple of t his is shown in List ing 12- 5 .
List in g 1 2 - 5 . An Ex a m ple of a Sim ple Er r or H a n dle r Public Sub MyEntryPoint() On Error GoTo ErrorHandler ' Your code here. ErrorExit: Exit Sub ErrorHandler: MsgBox Err.Description, vbCritical, "Application Name" Resume ErrorExit End Sub
Sim ple error handlers are appropriat e only for t he m ost t rivial applicat ions. An exam ple m ight be a ut ilit y add- in t hat provides a num ber of sim ple feat ures t hat require a sm all am ount of code and do not require any significant cleanup. The prim ary purpose of a sim ple error handler is t o shield users from raw runt im e errors such as t he one shown in Figure 12- 1.
Complex Project Error Handler Organization There are t wo or t hree com plex error handling syst em designs com m only used in Excel VBA applicat ions and several m inor variat ions on each of t hose. I f designed correct ly, all of t hem will accom plish t he sam e purpose; gracefully handling runt im e errors encount ered by your applicat ion. The com plex error handling syst em we int roduce in t his sect ion has t he following charact erist ics: All nont rivial procedures cont ain error handlers. All procedure error handlers call a cent ral error handling funct ion. This funct ion t racks and logs each error, decides whet her or not t o display an error m essage t o t he user and t ells t he calling procedure how t o proceed by way of it s ret urn value. All ent ry point procedures are subrout ines. An ent ry point procedure is any procedure in which code execut ion begins. This includes subrout ines in st andard m odules called by t oolbar but t ons and event procedures execut ed in response t o som e user act ion. All nont rivial lower- level procedures ( all procedures t hat are called by ent ry point procedures) are Boolean funct ions whose ret urn value indicat es whet her t he funct ion succeeded or failed. We cover all of t hese point s in det ail as t his sect ion progresses, but we want ed t o give you a highlevel overview of how our error handling syst em works. An im port ant point t o keep in m ind as you read t his sect ion is t hat ent ry point procedures m ust only be t riggered direct ly by som e user act ion. One ent ry point procedure m ust never call anot her ent ry point procedure or t he error handling syst em described here will break down. I f t wo ent ry point procedures need t o run t he sam e code, t he com m on code should be fact ored out int o a lowerlevel funct ion t hat can be called by bot h ent ry point procedures.
Procedure Error Handlers List ing 12- 6 shows t wo error handler procedure skelet ons. The first is an exam ple of an ent ry point subrout ine, t he second an exam ple of a lower- level, Boolean funct ion. We have placed a call from t he ent ry point subrout ine t o t he lower- level funct ion t o dem onst rat e how t he error handling syst em would work. We explain t he purpose of t he various const ant s shown in List ing 12- 6 as well as t he funct ion call inside t he error handlers in The Cent ral Error Handler sect ion lat er in t he chapt er .
List in g 1 2 - 6 . Su br ou t in e a n d Fu n ct ion Er r or H a n dle r s Private Const msMODULE As String = "MMyModule"
Public Sub MyEntryPointSubroutine() Const sSOURCE As String = "MyEntryPointSubroutine()" On Error GoTo ErrorHandler ' Call the lower level function. If Not bMyLowerLevelFunction() Then Err.Raise glHANDLED_ERROR End If ErrorExit: ' Cleanup code here. Exit Sub ErrorHandler: If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else Resume ErrorExit End If End Sub Private Function bMyLowerLevelFunction() As Boolean Const sSOURCE As String = "bMyLowerLevelFunction()" Dim bReturn As Boolean
' The function return value
On Error GoTo ErrorHandler ' Assume success until an error is encountered. bReturn = True ' Operational code here. ErrorExit: ' Cleanup code here. bMyLowerLevelFunction = bReturn Exit Function ErrorHandler: bReturn = False If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume Else
Resume ErrorExit End If End Function
The general layout of t he error handlers is very sim ilar in bot h cases. The only significant difference is t he funct ion m ust ret urn a value indicat ing success or failure wit hout violat ing t he single exit point principle, so we have added t he st ruct ure required t o accom plish t hat t o t he funct ion's error handler . List ing 12- 6 shows exam ples of very sim ple error handlers. They don't t ry t o respond t o errors ot her t han by invoking t he cent ral error handler and exit ing. I n m any sit uat ions, you will be aware of errors t hat m ight occur but can be correct ed in t he error handler and allow code execut ion t o cont inue. A m ore com plex error handler, such as t he one shown in List ing 12- 7, enables you t o accom plish t his.
List in g 1 2 - 7 . A M or e Com ple x Er r or H a n dle r ErrorHandler: Select Case Err.Number Case 58 ' File already exists. Resolve the problem and resume. Resume Case 71 ' Disk not ready. Resolve the problem and resume. Resume Case Else ' The error can't be resolved here. Invoke the central ' error handling procedure. If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then ' If the program is in debug mode, execution ' continues here. Stop Resume Else Resume ErrorExit End If End Select End Function
A Select Case st at em ent is used t o ident ify error num bers t hat can be handled wit hin t he error handler. I f t he num ber of t he error t rapped is not one of t hose handled by a specific Case clause, it falls t hrough t o t he Case Else clause, which invokes t he cent ral error handler.
N OTE I n t he cont ext of error handling, t he word t rap refers t o an error handler being act ivat ed by an error. I t is synonym ous wit h t he word cat ch, which is com m only used in it s place.
A t ypical error handling scenario based on t he exam ples shown in List ing 12- 6 and List ing 12- 7 would play out som et hing like t his. MyEnt ryPoint Subrout ine calls bMyLowerLevelFunct ion t o perform som e operat ion. An error occurs in bMyLowerLevelFunct ion t hat cannot be handled by it s error handler. The error handler in bMyLower LevelFunct ion calls t he cent ral error handler. The cent ral error handler logs t he error and passes a value back t o bMyLowerLevelFunct ion t hat t ells it t o exit . bMyLowerLevelFunct ion exit s and ret urns False t o t he MyEnt ryPoint Subrout ine calling procedure. I n MyEnt ryPoint Subrout ine, a cust om error is raised ( because bMyLowerLevelFunct ion ret urned False) , which t hen calls t he cent ral error handler again. Because an ent ry point subrout ine called t he cent ral error handler, an error m essage is displayed t o t he user. Aft er t he cent ral error handler has com plet ed it s dut ies, code execut ion resum es in MyEnt ryPoint Subrout ine, which t hen exit s. I n The Cent ral Error Handler sect ion below, we describe how t he cent ral error handler det erm ines when an error m essage should be displayed and how it influences program execut ion aft er t he error is handled.
Trivial Procedures At t he beginning of t his sect ion we st at ed t hat all n on t r iv ia l procedures cont ain error handlers. That begs t he quest ion of what is a t rivial procedure t hat wouldn't require an error handler. A t rivial procedure is eit her so sim ple t hat an error cannot occur wit hin it or is st ruct ured such t hat any errors t hat do occur are ignored. List ing 12- 8 shows exam ples of bot h t ypes.
List in g 1 2 - 8 . Tr ivia l Pr oce du r e s D on 't Re qu ir e Er r or H a n dle r s ' This subroutine is so simple that no errors ' will ever be generated within it. Public Sub ResetAppProperties() Application.StatusBar = False Application.ScreenUpdating = True Application.DisplayAlerts = True Application.EnableEvents = True Application.EnableCancelKey = xlInterrupt Application.Cursor = xlDefault End Sub ' Any errors that occur in this function are ignored. Private Function bIsBookOpen(ByVal sBookName As String, _
ByRef wkbBook As Workbook) As Boolean On Error Resume Next Set wkbBook = Application.Workbooks(sBookName) bIsBookOpen = (Len(wkbBook.Name) > 0) End Function
The Central Error Handler The cent ral error handler is t he heart of any com plex error handling syst em . I t consist s of a procedure designed t o log errors t o an error log file or ot her persist ent locat ion and display error m essages t o t he user, as well as provide facilit ies t hat allow t he program m er t o debug errors during developm ent . ( We cover debugging in det ail in Chapt er 16 VBA Debugging.) The m odule cont aining t he cent ral error handler also cont ains all error handling- relat ed const ant s, m aking t he error handling syst em fully encapsulat ed. List ing 12- 9 shows an exam ple of a com plet e cent ral error handler.
List in g 1 2 - 9 . A Ce n t r a l Er r or H a n dle r Public Const gbDEBUG_MODE As Boolean = False Public Const glHANDLED_ERROR As Long = 9999 Public Const glUSER_CANCEL As Long = 18 Private Const msSILENT_ERROR As String = "UserCancel" Private Const msFILE_ERROR_LOG As String = "Error.log" Public Function bCentralErrorHandler( _ ByVal sModule As String, _ ByVal sProc As String, _ Optional ByVal sFile As String, _ Optional ByVal bEntryPoint As Boolean) As Boolean Static sErrMsg As String Dim Dim Dim Dim Dim
iFile As Integer lErrNum As Long sFullSource As String sPath As String sLogText As String
' Grab the error info before it's cleared by ' On Error Resume Next below. lErrNum = Err.Number ' If this is a user cancel, set the silent error flag ' message. This will cause the error to be ignored. If lErrNum = glUSER_CANCEL Then sErrMsg = msSILENT_ERROR ' If this is the originating error, the static error ' message variable will be empty. In that case, store ' the originating error message in the static variable. If Len(sErrMsg) = 0 Then sErrMsg = Err.Description
' We cannot allow errors in the central error handler. On Error Resume Next ' Load the default filename if required. If Len(sFile) = 0 Then sFile = ThisWorkbook.Name ' Get the application directory. sPath = ThisWorkbook.Path If Right$(sPath, 1) "\" Then sPath = sPath & "\" ' Construct the fully-qualified error source name. sFullSource = "[" & sFile & "]" & sModule & "." & sProc ' Create the error text to be logged. sLogText = " " & sFullSource & ", Error " & _ CStr(lErrNum) & ": " & sErrMsg ' Open the log file, write out the error information and ' close the log file. iFile = FreeFile() Open sPath & msFILE_ERROR_LOG For Append As #iFile Print #iFile, Format$(Now(), "mm/dd/yy hh:mm:ss"); sLogText If bEntryPoint Then Print #iFile, Close #iFile ' Do not display silent errors. If sErrMsg msSILENT_ERROR Then ' Show the error message when we reach the entry point ' procedure or immediately if we are in debug mode. If bEntryPoint Or gbDEBUG_MODE Then Application.ScreenUpdating = True MsgBox sErrMsg, vbCritical, gsAPP_TITLE ' Clear the static error message variable once ' we've reached the entry point so that we're ready ' to handle the next error. sErrMsg = vbNullString End If ' The return value is the debug mode status. bCentralErrorHandler = gbDEBUG_MODE Else ' If this is a silent error, clear the static error ' message variable when we reach the entry point. If bEntryPoint Then sErrMsg = vbNullString bCentralErrorHandler = False End If End Function
This is a lot t o digest , so let 's dissect it piece by piece. First t he const ant declarat ions: gbDEBUG_MODE This public Boolean const ant is used by t he developer t o set t he debug m ode st at us of t he applicat ion. When you are t est ing your applicat ion or at t em pt ing t o locat e errors in your code, you want your error handlers t o behave different ly t han t hey do when your applicat ion is deployed t o end users. Set t ing t he gbDEBUG_MODE const ant t o True causes t he cent ral error handler funct ion t o display an error m essage im m ediat ely aft er an error occurs and t hen ret urn True. As shown in List ing 12- 6 and List ing 12- 7, when t he cent ral error handler funct ion ret urns True, t he procedure error handler drops int o a VBA St op st at em ent followed by a Resum e st at em ent . The St op st at em ent put s t he proj ect int o Break m ode and t he Resum e st at em ent enables you t o single st ep back t o t he line of code in t he procedure where t he error occurred. You can t hen debug t he error. Again, we discuss debugging in det ail in Chapt er 16 VBA Debugging. glHANDLED_ERROR This public Long const ant is an error num ber you can use t o raise cust om errors. As we discussed in t he sect ion on raising cust om errors, when you raise a cust om error you m ust supply an error num ber not already used by Excel or VBA. The glHANDLED_ERROR const ant has a value of 9999, which is not wit hin t he range of error num ber values used by VBA. I t also has t he advant age of being easily recognizable as a cust om error num ber for debugging purposes. I n all but t he m ost com plex error handling scenarios, a single cust om error num ber can be used for all of your cust om errors. glUSER_CANCEL This public Long const ant is set t o t he VBA error value 18. This error value occurs when t he user cancels program execut ion by pressing t he Esc or Ct rl+ Break keys. Unless it is absolut ely crit ical t hat your program not be int errupt ed, such as during st art up and shut down, you should always allow t he user t o halt program execut ion. The best way t o do t his is t o add t he following st at em ent at t he beginning of each ent ry point procedure: Application.EnableCancelKey = xlErrorHandler
This will cause VBA t o t reat a user cancel as a runt im e error wit h an Err.Number = 18 t hat is rout ed t hrough your error handler. When t he cent ral error handler sees t his error num ber, it convert s it int o a special error m essage st ring, which we cover next , t hat causes t he error t o be ignored. m sSI LENT_ERROR This privat e St ring const ant is assigned t o t he st at ic error m essage variable in t he cent ral error handling funct ion whenever a user cancel error ( Err.Number = glUSER_ CANCEL) is det ect ed. Because t his error m essage variable is st at ic, it holds it s value bet ween calls t o t he cent ral error handler. This m eans no m at t er how deep in t he call st ack t he program was when t he user cancelled execut ion, t he error handler will pass t he error up t he st ack and out t he ent ry point procedure wit hout displaying an error m essage t o t he user. Silent errors also will not t rigger t he procedure debugging m echanism , even if t he applicat ion is in debug m ode. m sFI LE_ERROR_LOG This privat e St ring const ant specifies t he nam e of t he t ext file t o which all error m essages will be writ t en. The error log file will always be locat ed in t he sam e
direct ory as t he workbook cont aining t he cent ral error handler. The inform at ion st ored in t he error log file is designed t o help you debug errors t hat have occurred on a user's com put er t hat you m ay not necessarily have access t o. The error log file will show you t he exact error m essage, t he procedure where t he error originat ed and t he call st ack t hat led t o t he error. Com bined wit h a brief verbal report from t he user about exact ly what t hey were doing when t he error occurred, t his inform at ion is usually sufficient t o enable you t o debug t he problem . Now we exam ine t he code in t he cent ral error handler funct ion line by line t o see how an error is t reat ed under various condit ions. First let 's look at t he argum ent s t o t he funct ion. The first t hree argum ent s t o t he bCent ralErrorHandler funct ion ident ify t he code m odule, procedure and filenam e from which t he funct ion was called. This inform at ion is writ t en t o t he error log file for use in debugging runt im e errors. The fourt h argum ent t o t he bCent ralErrorHandler funct ion indicat es whet her or not it was called from an ent ry point procedure. I f t he applicat ion is not in debug m ode, an error m essage is displayed t o t he user only when t he error reaches t he originat ing ent ry point procedure. I f t he applicat ion is in debug m ode, t he fourt h argum ent is ignored, an error m essage is displayed im m ediat ely and t he cent ral error handler ret urns False so t hat you can begin debugging t he error. Not ice t hat wit hin t he bCent ralErrorHandler funct ion we have declared a st at ic St ring variable. This variable is used t o st ore t he original error m essage so we can display it t o t he user when we reach t he ent ry point , regardless of how m any procedures deep in t he st ack we are when t he error occurs. This st at ic variable will hold it s value unt il we explicit ly change it . As soon as code execut ion has ent ered t he bCent ralErrorHandler funct ion, we m ust read and st ore any inform at ion we need from t he VBA Err obj ect . The reason for t his will becom e apparent very short ly.
lErrNum = Err.Number ' If this is a user cancel, set the silent error flag ' message. This will cause the error to be ignored. If lErrNum = glUSER_CANCEL Then sErrMsg = msSILENT_ERROR ' If this is the originating error, the static error ' message variable will be empty. In that case, store ' the originating error message in the static variable. If Len(sErrMsg) = 0 Then sErrMsg = Err.Description
First we read and st ore t he error num ber. Then, if t he error num ber indicat es t hat t he user has cancelled program execut ion, we st ore t he gsSI LENT_ERROR flag m essage in our st at ic error m essage variable. I f t he error num ber does not indicat e a user cancel and t he st at ic error m essage variable does not already cont ain a value, we st ore t he error descript ion in t he st at ic error m essage variable. I n t his way, we st ore only t he original error m essage and persist it t hrough any addit ional calls t o t he cent ral error handler funct ion. The st at ic variable is cleared only aft er t he ent ry point procedure has been reached and t he error m essage displayed t o t he user. The reason we m ust persist any necessary Err obj ect values im m ediat ely upon ent ering t he cent ral error handler funct ion is because we cannot allow any errors t o occur in t his funct ion. Therefore t he ent ire funct ion is wrapped in On Error Resume Next:
' We cannot allow errors in the central error handler.
On Error Resume Next
As soon as code execut ion passes t he On Error Resume Next st at em ent , all propert ies of t he Err obj ect are aut om at ically cleared. I f you have not st ored t he original values from t he Err obj ect 's propert ies at t his point , t hey are lost forever. I n t he next sect ion of code we const ruct several St ring values t hat t he error handler requires.
' Load the default filename if required. If Len(sFile) = 0 Then sFile = ThisWorkbook.Name ' Get the application directory. sPath = ThisWorkbook.Path If Right$(sPath, 1) "\" Then sPath = sPath & "\" ' Construct the fully-qualified error source name. sFullSource = "[" & sFile & "]" & sModule & "." & sProc ' Create the error text to be logged. sLogText = " " & sFullSource & ", Error " & _ CStr(lErrNum) & ": " & sErrMsg
You will not ice from List ing 12- 9 t hat t he sFile argum ent is opt ional. I f no value for t his argum ent is passed, t he cent ral error handler assum es it is being called from wit hin t he current workbook and it loads t his argum ent 's value wit h ThisWorkbook.Name. The next t ask is t o get t he pat h t o t he current workbook. This is where t he error log file will be creat ed ( or updat ed if it has already been creat ed) . I n t he next line of code we const ruct a fully qualified locat ion t hat ident ifies where t he call t o t he bCent ralErrorHandler funct ion originat ed. This locat ion ident ifier has t he following form at :
[FileName]CodeModuleName.ProcedureName
The last st ring we const ruct in t his sect ion is t he com plet e error log file ent ry. This consist s of t he fully qualified locat ion st ring creat ed above, prefixed wit h t he dat e and t im e t he error occurred and suffixed wit h t he error num ber and t he error m essage. We will see ex am ples of error log file ent ries lat er in t his chapt er. Our next t ask is t o writ e t he ent ry in t he applicat ion error log file. As shown below, we use st andard VBA file I / O t echniques t o creat e or append t o t he error log file:
' Open the log file, write out the error information and ' close the log file. iFile = FreeFile() Open sPath & msFILE_ERROR_LOG For Append As #iFile Print #iFile, Format$(Now(), "mm/dd/yy hh:mm:ss"); sLogText
If bEntryPoint Then Print #iFile, Close #iFile
We first acquire an available file num ber and use it t o creat e or open t he error log file specified by t he m sFI LE_ERROR_LOG const ant and locat ed in t he pat h creat ed in t he previous sect ion. We t hen writ e t he log file ent ry st ring creat ed above t o t he log file. I f t he bCent ralErrorHandler funct ion has been called by an ent ry point procedure, we writ e an addit ional blank line t o t he error log file t o provide visual separat ion bet ween t his and subsequent errors. Aft er t hat we close t he error log file. The last sect ion of t he cent ral error handler det erm ines whet her and when an error m essage is displayed and whet her t he cent ral error handler t riggers debug m ode behavior in t he procedure t hat called it .
' Do not display or debug silent errors. If sErrMsg msSILENT_ERROR Then ' Show the error message when we reach the entry point ' procedure or immediately if we are in debug mode. If bEntryPoint Or gbDEBUG_MODE Then Application.ScreenUpdating = True MsgBox sErrMsg, vbCritical, gsAPP_TITLE ' Clear the static error message variable once ' we've reached the entry point so that we're ready ' to handle the next error. sErrMsg = vbNullString End If ' The return vale is the debug mode status. bCentralErrorHandler = gbDEBUG_MODE Else ' If this is a silent error, clear the static error ' message variable when we reach the entry point. If bEntryPoint Then sErrMsg = vbNullString bCentralErrorHandler = False End If
The prim ary deciding fact or on when t o display an error m essage and when t o ignore it is t he value of t he st at ic sErrMsg variable. Rem em ber t hat t his variable holds t he value of t he original error m essage t hat t riggered t he cent ral error handler. I f t he value of t his variable indicat es t hat t he original error was t he result of t he user canceling program execut ion, t hen no error m essage is displayed, t he st at ic error m essage variable is cleared t o prepare t he cent ral error handler for t he next error, and t he ret urn value of t he cent ral error handler is False, so as not t o t rigger any debug act ions. I f t he st at ic error m essage variable indicat es any error ot her t han a user cancel error, t hen an error m essage is displayed. I f t he applicat ion is in debug m ode ( gbDEBUG_MODE = True) , an error
m essage displays as soon as t he error occurs and t he cent ral error handler ret urns True so t he calling procedure can begin execut ing debug code. I f t he applicat ion is not in debug m ode, an error m essage displays only when t he error handling code reaches t he ent ry point procedure. I n t his case, t he cent ral error handler funct ion ret urns False t hroughout so as not t o t rigger any procedure- level debug code.
Error Handling in Classes and Userforms Classes and userform s present som e unique error handling challenges t hat we cover in t his sect ion. As explained previously, event procedures in classes and userform s should alm ost always be considered ent ry point procedures. The I nit ialize, Act ivat e and Term inat e event s are except ions t o t his rule. The user does not direct ly t rigger t hese event s. I nst ead, t hey are fired as a side effect of a class being creat ed or dest royed or a userform being creat ed, shown or dest royed. Error handling for t hese event s is a lit t le t ricky, so we discuss t hem in det ail.
Initialize and Activate Events Errors t hat occur in I nit ialize or Act ivat e event s are t ypically cat ast rophic errors t hat render t he class or userform in which t hey occur unusable. Therefore, t hey norm ally cannot be handled in any way t hat would m it igat e t hem . I f you are going t o place code in eit her of t hese event procedures, t he best opt ion is not t o give t hem an error handler at all. This will delegat e t he handling of any errors t hat occur inside t hese event procedures t o t he error handler of t he procedure t hat at t em pt ed t o creat e t he obj ect . An opt ion t hat gives you m uch m ore cont rol over t he init ializat ion process, and any errors t hat arise as a result of it , is t o creat e your own cust om I nit ialize m et hod. This Boolean funct ion would replace t he I nit ialize and Act ivat e event procedures, so t hose event s would no longer need t o be t rapped in your code. I n t he Put t ing I t All Toget her sect ion below, we show an exam ple of a cust om I nit ialize m et hod in a userform .
Terminate Events Errors t hat occur in Term inat e event s are unusual in t hat , assum ing proper program m ing t echniques have been used, neit her are t hey cat ast rophic nor can t hey be m it igat ed. When t he Term inat e event is fired, t he class or userform has perform ed it s funct ion and is being dest royed. I f you need t o place code in t he Term inat e event of a class or userform , it is best t o sim ply ignore any errors t hat occur by using t he On Error Resume Next st at em ent at t he beginning of t he procedure.
Putting It All Together Alt hough we've described all t he pieces of an error handling syst em , it m ay not be clear how all t hose pieces fit t oget her. I n t his sect ion we show a sm all but com plet e program t hat dem onst rat es t he basic error handling t echniques. This program is adm it t edly cont rived, but t he idea behind it is t o have a com plet e program wit h as lit t le dist ract ion from non- error- handling- relat ed code as possible. The com plet e program can be found in t he Concept s folder of t he CD in t he workbook nam ed ErrorHandlingDem o.xls. The error handling dem o program consist s of a single ent ry point procedure t hat displays a userform and t hen calls a funct ion t hat int ent ionally generat es an error depending on whet her t he user clicks t he OK or Cancel but t on on t he userform . Figure 12- 2 shows t he userform for our error handling dem o and List ing 12- 10 shows t he code behind t his userform .
List in g 1 2 - 1 0 . Th e Code Be h in d t h e Er r or H a n dlin g D e m o Use r for m Private Const msMODULE As String = "FDemo" Private bUserCancel As Boolean Public Property Get UserCancel() As Boolean UserCancel = bUserCancel End Property Private Sub cmdOK_Click() bUserCancel = False Me.Hide End Sub Private Sub cmdCancel_Click() bUserCancel = True Me.Hide End Sub Private Sub UserForm_QueryClose(Cancel As Integer, _ CloseMode As Integer) ' Route any X-close button calls through ' the cmdCancel_Click procedure. If CloseMode = vbFormControlMenu Then Cancel = True cmdCancel_Click End If End Sub
Public Function Initialize() As Boolean Const sSOURCE As String = "Initialize()" Dim bReturn As Boolean
' The function return value
On Error GoTo ErrorHandler ' Assume success until an error is encountered. bReturn = True ' Set the UserForm caption. Me.Caption = gsAPP_TITLE ErrorExit: Initialize = bReturn Exit Function ErrorHandler: bReturn = False If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume Else Resume ErrorExit End If End Function
Figu r e 1 2 - 2 . Th e Er r or H a n dlin g D e m o Use r for m
The first t hing t o not ice is t he userform has a read- only UserCancel propert y. The value of t his propert y is det erm ined by which but t on t he user clicks. I f t he OK but t on is clicked, t he UserCancel propert y will ret urn False ( m eaning t he user did not cancel t he userform ) . I f t he Cancel but t on is clicked, t he UserCancel propert y will ret urn True. I n t he code for t he calling procedure we will dem onst rat e how t o raise a cust om user cancel error in response t o t he UserCancel m et hod ret urning True t hat will cause t he error handler t o exit silent ly rat her t han displaying an error. The second t hing t o not ice is we are t rapping clicks t o t he X- close but t on on t he userform wit h t he UserForm _QueryClose event procedure and rerout ing t hem t o t he cm dCancel_Click event procedure. This m akes a click on t he X- close but t on behave exact ly like a click on t he Cancel but t on. The last t hing t o not ice is t he userform cont ains a cust om I nit ialize m et hod. This m et hod is a Boolean funct ion t hat ret urns True if init ializat ion succeeds and False if an error occurred during init ializat ion. This m et hod is called prior t o showing t he userform . The calling funct ion t hen exam ines t he ret urn value of t he m et hod and does not at t em pt t o show t he userform if init ializat ion failed. List ing 12- 11 shows t he funct ion t hat will purposely cause an error.
List in g 1 2 - 1 1 . Th e bCa u se An Er r or Fu n ct ion Public Function bCauseAnError() As Boolean Const sSOURCE As String = "bCauseAnError()" Dim bReturn As Boolean Dim lTest As Long
' The function return value
On Error GoTo ErrorHandler ' Assume success until an error is encountered. bReturn = True ' Cause a divide by zero error. lTest = 1 / 0 ErrorExit: bCauseAnError = bReturn Exit Function ErrorHandler: bReturn = False If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume Else Resume ErrorExit End If
End Function
This funct ion is exact ly t he sam e as t he one we showed in List ing 12- 6 wit h som e code added t hat causes it t o t hrow a divide by zero error. Now we can t ie t hings t oget her wit h t he ent ry point procedure t hat runs t he applicat ion. The code for t his procedure is shown in List ing 12- 12.
List in g 1 2 - 1 2 . Th e En t r yPoin t Su br ou t in e Public Sub EntryPoint() Const sSOURCE As String = "EntryPoint" Dim bUserCancel As Boolean Dim frmDemo As FDemo On Error GoTo ErrorHandler Set frmDemo = New FDemo Load frmDemo ' If UserForm initialization failed, raise a custom error. If Not frmDemo.Initialize() Then Err.Raise glHANDLED_ERROR frmDemo.Show ' If the user pressed the Cancel button, raise a custom ' user cancel error. This will cause the central error ' handler to exit the program without displaying an ' error message. If frmDemo.UserCancel Then Err.Raise glUSER_CANCEL ' If the user pressed the OK button, run the function that ' is designed to cause an error. If Not bCauseAnError() Then Err.Raise glHANDLED_ERROR ErrorExit: ' Clean up the UserForm Unload frmDemo Set frmDemo = Nothing Exit Sub ErrorHandler: If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else Resume ErrorExit End If
End Sub
The Ent ryPoint subrout ine is run from a but t on locat ed on Sheet 1 of t he ErrorHandlingDem o.xls workbook. This applicat ion has only t wo possible execut ion pat hs. Clicking t he OK but t on on t he userform t riggers t he first and clicking t he Cancel but t on on t he userform t riggers t he second. Let 's exam ine what happens in each case and see t he result ing error log ent ries. The Ent ryPoint subrout ine first creat es a new inst ance of t he FDem o UserForm , loads it and calls t he userform 's cust om I nit ialize m et hod. I n t his sam ple applicat ion t he userform will never fail t o init ialize. We have provided t his cust om I nit ialize m et hod t o dem onst rat e how you would init ialize a userform in a way t hat is linked int o t he error handling syst em . Next , t he Ent ryPoint subrout ine shows t he FDem o userform . As you can see in Figure 12- 2, t he only act ions available t o t he user are clicking t he OK or Cancel but t ons. Clicking t he OK but t on set s t he FDem o userform 's UserCancel propert y t o False, m eaning t he user did not cancel. Clicking t he Cancel but t on set s t he UserCancel propert y t o True, m eaning t he user did cancel. Clicking eit her but t on also hides t he userform , allowing t he Ent ryPoint subrout ine t o cont inue execut ing. Because t he FDem o userform is hidden rat her t han unloaded, when code execut ion ret urns t o t he Ent ryPoint subrout ine t he userform is st ill in m em ory. This allows t he Ent ryPoint subrout ine t o check t he value of t he FDem o UserCancel propert y t o det erm ine what t he user has asked it t o do. I f t he UserCancel propert y is True, t he Ent ryPoint subrout ine needs t o exit wit hout displaying an error m essage but st ill running it s cleanup code. I t accom plishes t his by raising a cust om user cancel error. I f you recall from t he discussion of t he cent ral error handler, VBA uses t he error num ber 18 t o indicat e t he user has cancelled program execut ion, we have defined a public const ant t hat holds t his value, and when t he cent ral error handler sees a user cancel error it exit s silent ly. Therefore, t o exit as a result of t he user clicking Cancel in t he FDem o userform , t he Ent ryPoint subrout ine raises a cust om error wit h t he error num ber glUSER_ CANCEL. The line of code used t o accom plish t his is shown here:
If frmDemo.UserCancel Then Err.Raise glUSER_CANCEL
This not ifies t he cent ral error handler of t he error. The cent ral error handler logs t he error and ret urns cont rol t o t he Ent ryPoint procedure so it can com plet e it s cleanup act ivit ies prior t o exit ing. The cent ral error handler records all errors, including user cancel errors, in t he error log. The error.log file will be locat ed in t he sam e direct ory as t he ErrorHandlingDem o.xls workbook. The ent ry m ade in response t o t he user clicking t he FDem o Cancel but t on will be sim ilar t o t he ent ry shown below except it will be writ t en t o a single line in t he error log file:
03/30/04 20:23:37 [ErrorHandlingDemo.xls] MEntryPoints.EntryPoint, Error 18: UserCancel
I f t he user did not cancel program execut ion, t he Ent ryPoint subrout ine cont inues wit h t he next line
of code. This line is a call t o t he funct ion t hat is designed t o int ent ionally t hrow a divide by zero error. As you can see in List ing 12- 11, t his funct ion's error handler will first call t he cent ral error handler t o not ify it of t he error, t hen cause t he funct ion t o ret urn False in order t o not ify t he calling procedure t hat an error has occurred. I n t his case, t he error is cat ast rophic, so t he calling procedure m ust t erm inat e t he program . I t does t his by raising a cust om handled error, as shown below :
If Not bCauseAnError() Then Err.Raise glHANDLED_ERROR
Because t his error was raised from an ent ry point procedure, t he original error m essage st ored by t he cent ral error handler will be displayed t o t he user, as shown in Figure 12- 3.
Figu r e 1 2 - 3 . Th e Er r or M e ssa ge D ispla ye d t o t h e Use r
I n t his case t he cent ral error handler will log t wo ent ries: one from t he funct ion where t he error originat ed and one from t he ent ry point procedure.
03/30/04 20:44:20 [ErrorHandlingDemo.xls] MSystemCode.bCauseAnError(), Error 11: Division by zero 03/30/04 20:44:20 [ErrorHandlingDemo.xls] MEntryPoints.EntryPoint, Error 9999: Division by zero
Not e t hat t he first error num ber recorded is t he original VBA error num ber, while t he second error num ber ( and any subsequent error num bers) is t he value of our predefined glHANDLED_ERROR const ant . I f t here are m ult iple procedures in t he call st ack when an error occurs, t he cent ral error handler will creat e a log ent ry for each one. This provides helpful inform at ion when debugging an error because it provides a record of t he call st ack at t he t im e t he error occurred. Aft er t he error has been logged and t he error m essage displayed, t he cent ral error handler ret urns cont rol t o t he Ent ryPoint subrout ine so it can com plet e it s cleanup prior t o exit ing.
Practical Example PETRAS Timesheet I n t he Pract ical Exam ple sect ion of t his chapt er, we ret rofit our t im e- ent ry add- in wit h a com plet e cent ralized error handling syst em . This is t he best exam ple t o exam ine if you want t o see how a real- world error handling syst em is const ruct ed. The process of ret rofit t ing our add- in wit h error handling is t edious but uncom plicat ed. All ent ry point procedures are out fit t ed wit h t he ent ry point version of t he error handling code and all subprocedures are convert ed int o Boolean funct ions and out fit t ed wit h t he funct ion version of t he error handling code. The only code exam ple from t he new version of t he PETRAS add- in t hat we show here is t he Aut o_Open procedure, in List ing 12- 13. This is t he ent ry point procedure t hat m akes t he m ost calls t o lower- level procedures. I t also has t he unique requirem ent t o shut down t he applicat ion if an error occurs. This m akes it t he m ost int erest ing exam ple of error handling in t he add- in. You are encouraged t o exam ine t he com plet e revised code for t he PETRAS add- in, locat ed on t he CD in t he Applicat ion folder for t his chapt er, for a com plet e view of t he error handling syst em .
List in g 1 2 - 1 3 . Th e PETRAS Add- in Au t o_ Ope n Pr oce du r e w it h Er r or H a n dlin g Public Sub Auto_Open() Const sSOURCE As String = "Auto_Open" Dim bErrorOut As Boolean Dim wkbBook As Workbook ' The very first thing your application should do upon ' startup is attempt to delete any copies of its ' command bars that may have been left hanging around ' by an Excel crash or other incomplete exit. On Error Resume Next Application.CommandBars(gsBAR_TOOLBAR).Delete On Error GoTo ErrorHandler ' Initialize global variables. If Not bInitGlobals() Then Err.Raise glHANDLED_ERROR ' Assume False until an error is encountered. bErrorOut = False
' Make sure we can locate our time entry workbook before we ' do anything else. If Len(Dir$(gsAppDir & gsFILE_TIME_ENTRY)) = 0 Then _ Err.Raise glHANDLED_ERROR, sSOURCE, gsERR_FILE_NOT_FOUND Application.ScreenUpdating = False Application.EnableEvents = False Application.StatusBar = gsSTATUS_LOADING_APP ' Build the command bars. If Not bBuildCommandBars() Then Err.Raise glHANDLED_ERROR ' Set the initial state of the application. If Not gclsEventHandler.SetInitialStatus() Then _ Err.Raise glHANDLED_ERROR ErrorExit: ' Reset critical application properties. ResetAppProperties ' If an error occurred during the Auto_Open procedure, ' the only option is to exit the application. If bErrorOut Then ShutdownApplication Exit Sub ErrorHandler: ' This variable informs the clean up section when an error ' has occurred. bErrorOut = True If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else Resume ErrorExit End If End Sub
This version of t he Aut o_Open procedure is very different from t he version we last saw in Chapt er 8 Advanced Com m and Bar Handling. You will not ice t hat wit h t he except ion of t he Reset AppPropert ies procedure and t he Shut downApplicat ion procedure, every procedure called by Aut o_Open is now a Boolean funct ion whose ret urn value indicat es success or failure. The Reset AppPropert ies procedure is an except ion because it is t he rare case of a procedure in which not hing can go wrong. This t ype of procedure was described in t he Trivial Procedures sect ion above and t he Reset AppPropert ies procedure it self was shown in List ing 12- 8. The Shut downApplicat ion procedure is an except ion because it is t he last procedure run before t he applicat ion closes. Sim ilar t o t he bCent ralErrorHandler funct ion we exam ined in List ing 12- 9, it doesn't m ake any sense t o t ry and handle errors t hat occur in t his procedure, so t he ent ire
Shut downApplicat ion procedure is wrapped in On Error Resume Next. We've also added a new bErrorOut flag variable. This is because t he cleanup sect ion for t he Aut o_Open procedure ( t he sect ion of code bet ween t he ErrorExit label and t he Exit Sub st at em ent ) needs t o know whet her an error has occurred when it is execut ed. The error handler for t he Aut o_Open procedure set s t he bErrorOut variable t o True when an error occurs. I t t hen calls t he cent ral error handler and, aft er t he cent ral error handler ret urns, it redirect s code execut ion t o t he cleanup sect ion, st art ing direct ly below t he ErrorExit label. I f t he bErrorOut variable indicat es t o t he cleanup sect ion t hat an error has occurred, t he cleanup sect ion init iat es applicat ion shut down by calling t he Shut downApplicat ion procedure.
PETRAS Reporting As m ent ioned in t he sect ion Com plex Proj ect Error Handler Organizat ion earlier in t his chapt er, t here are t wo or t hree com plex error handling syst em designs com m only used in Excel VBA applicat ions and several m inor variat ions on each of t hose. Throughout t his chapt er, we've dem onst rat ed t he concept s of error handling using a syst em known as t he fu n ct ion r e t u r n va lu e m et hod. I n t his syst em , every subprocedure is writ t en as a Boolean funct ion whose ret urn value indicat es success or failure. I f an error occurs, it is t rapped in t he funct ion's error handler, which logs t he error and t hen set s t he funct ion's ret urn value t o False. The calling procedure t est s t he ret urn value and ( usually) raises anot her error t o t rigger it s own error handler, and so t he error bubbles up t he call st ack. List ing 12- 14 shows t he order in which lines are execut ed in a nest ed set of procedures.
List in g 1 2 - 1 4 . Th e Or de r of Ex e cu t ion W h e n Usin g t h e Fu n ct ion Re t u r n Va lu e Syst e m Sub EntryPoint() Const sSOURCE As String = "EntryPoint" 1
On Error GoTo ErrorHandler
2 20
If Not bSubProc1() Then Err.Raise glHANDLED_ERROR End If
ErrorExit: 'Run some cleanup code 23 Exit Sub ErrorHandler: 21 If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else 22 Resume ErrorExit End If End Sub
Function bSubProc1() As Boolean Const sSOURCE As String = "bSubProc1" Dim bReturn As Boolean 3 4
On Error GoTo ErrorHandler bReturn = True
5 14
If Not bSubProc2() Then Err.Raise glHANDLED_ERROR End If
ErrorExit: 'Run some cleanup code 18 bSubProc1 = bReturn 19 Exit Function ErrorHandler: 15 bReturn = False 16 If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume Else 17 Resume ErrorExit End If End Function Function bSubProc2() As Boolean Const sSOURCE As String = "bSubProc2" Dim bReturn As Boolean 6 7
On Error GoTo ErrorHandler bReturn = True
8
'Cause an error Debug.Print 1 / 0
ErrorExit: 'Run some cleanup code 12 bSubProc2 = bReturn 13 Exit Function ErrorHandler: 9 bReturn = False 10 If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume Else 11 Resume ErrorExit End If
End Function
You'll not ice t hat in t he vast m aj orit y of cases, t he calling procedure handles a False ret urn value j ust by raising anot her error t o t rigger it s own error handler. Error handling in VBA is designed such t hat any unhandled errors and any errors raised wit hin an error handler aut om at ically fire t he error handler of t he calling procedure. So if we raise an error wit hin t he subprocedure's error handler, it will aut om at ically t rigger t he calling procedure's error handler, wit hout t he calling procedure having t o t est for a False ret urn value and t rigger t he error handler it self. The sam e will happen if we raise an error at t he end of t he cent ral error handler. This is known as t he r e - t h r ow syst em of error handling and has been im plem ent ed in t he PETRAS report ing applicat ion. The m ain advant ages of t he re- t hrow syst em are t hat we can use it wit hin Sub, Propert y and Funct ion procedures, and our funct ions' ret urn values can be used for t heir result s inst ead of success/ failure indicat ors. The m ain disadvant age is t hat it becom es slight ly harder for us t o include com plex cleanup code if an error occurs. List ing 12- 15 is t aken from t he MErrorHandler m odule of t he PETRASReport ing.xla workbook and shows a m odified cent ral error handler t hat im plem ent s t he re- t hrow syst em by default , but enables us t o override t he re- t hrow behavior in t he except ional cases when we need t o run com plex post - error cleanup code. This is very sim ilar t o List ing 12- 9, wit h t he ext ra code t o im plem ent t he re- t hrow m et hod highlight ed.
List in g 1 2 - 1 5 . A Ce n t r a l Er r or H a n dle r I m ple m e n t in g t h e Re - Th r ow Sy st e m Public Function bCentralErrorHandler( _ ByVal sModule As String, _ ByVal sProc As String, _ Optional ByVal sFile As String, _ Optional ByVal bEntryPoint As Boolean = False, _ Optional ByVal bReThrow As Boolean = True) As Boolean Static sErrMsg As String Dim Dim Dim Dim Dim
iFile As Integer lErrNum As Long sFullSource As String sPath As String sLogText As String
' Grab the error info before it's cleared by ' On Error Resume Next below. lErrNum = Err.Number ' If this is a user cancel, set the silent error flag ' message. This will cause the error to be ignored. If lErrNum = glUSER_CANCEL Then sErrMsg = msSILENT_ERROR
' If this is the originating error, the static error ' message variable will be empty. In that case, store ' the originating error message in the static variable. If Len(sErrMsg) = 0 Then sErrMsg = Err.Description ' We cannot allow errors in the central error handler. On Error Resume Next ' Load the default filename if required. If Len(sFile) = 0 Then sFile = ThisWorkbook.Name ' Get the application directory. sPath = ThisWorkbook.Path If Right$(sPath, 1) "\" Then sPath = sPath & "\" ' Construct the fully qualified error source name. sFullSource = "[" & sFile & "]" & sModule & "." & sProc ' Create the error text to be logged. sLogText = " " & sFullSource & ", Error " & _ CStr(lErrNum) & ": " & sErrMsg ' Open the log file, write out the error information and ' close the log file. iFile = FreeFile() Open sPath & msFILE_ERROR_LOG For Append As #iFile Print #iFile, Format$(Now(), "dd mmm yy hh:mm:ss"); sLogText If bEntryPoint Or Not bReThrow Then Print #iFile, Close #iFile ' Do not display or debug silent errors. If sErrMsg msSILENT_ERROR Then ' Show the error message when we reach the entry point ' procedure or immediately if we are in debug mode. If bEntryPoint Or gbDEBUG_MODE Then Application.ScreenUpdating = True MsgBox sErrMsg, vbCritical, gsAPP_TITLE ' Clear the static error message variable once ' we've reached the entry point so that we're ready ' to handle the next error. sErrMsg = vbNullString End If ' The return vale is the debug mode status. bCentralErrorHandler = gbDEBUG_MODE Else ' If this is a silent error, clear the static error ' message variable when we reach the entry point. If bEntryPoint Then sErrMsg = vbNullString bCentralErrorHandler = False
End If 'If we're using re-throw error handling, 'this is not the entry point and we're not debugging, 're-raise the error, to be caught in the next procedure 'up the call stack. 'Procedures that handle their own errors can call the 'central error handler with bReThrow:=False to log the 'error, but not re-raise it. If bReThrow Then If Not bEntryPoint And Not gbDEBUG_MODE Then On Error GoTo 0 Err.Raise lErrNum, sFullSource, sErrMsg End If Else 'Error is being logged and handled, 'so clear the static error message variable sErrMsg = vbNullString End If End Function
List ing 12- 16 shows t he order in which lines are execut ed in a nest ed set of procedures when t he re- t hrow syst em is im plem ent ed.
List in g 1 2 - 1 6 . Th e Or de r of Ex e cu t ion W h e n Usin g t h e Re - Th r ow Syst e m Sub EntryPoint() Const sSOURCE As String = "EntryPoint" 1
On Error GoTo ErrorHandler
2
SubProc1
ErrorExit: 11 Exit Sub ErrorHandler: 'Run simple cleanup code here 9
10
If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else Resume ErrorExit End If
End Sub Sub SubProc1() Const sSOURCE As String = "SubProc1" 3
On Error GoTo ErrorHandler
4
SubProc2 Exit Sub
ErrorHandler: 'Run simple cleanup code here 8
If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume End If End Sub Sub SubProc2() Const sSOURCE As String = "bSubProc2" 5
On Error GoTo ErrorHandler
6
'Cause an error Debug.Print 1 / 0 Exit Sub
ErrorHandler: 'Run simple cleanup code here 7
If bCentralErrorHandler(msMODULE, sSOURCE) Then Stop Resume End If End Sub
Using t he re- t hrow m et hod, we can only include cleanup code at t he st art of our error handlers ( before t he call t o t he cent ral error handler) , so we have t o be ext rem ely careful t o ensure t hat t he cleanup code does not cause any m ore errors t o occur and does not reset t he Err obj ect . I n pract ice, t his m eans t he re- t hrow m et hod is best used when t here is no cleanup required, or when t he cleanup is t rivial and could not cause an error. I n List ing 12- 15, we added an opt ional param et er t o t he cent ral error handler t hat enables us t o st op t he error being re- raised. This result s in exact ly t he sam e behavior as t he funct ion ret urn value syst em , t hereby allowing us t o use t hat m et hod in t he except ional cases t hat require com plex
cleanup code. This param et er is used in t he Consolidat eWorkbooks procedure t o handle errors t hat occur while ext ract ing t he dat a from a t im esheet workbook. I n t hat case, we call t he cent ral error handler t o log t he error, t hen close t he problem t im esheet workbook and cont inue wit h t he next one. Whet her t o use t he funct ion ret urn value or re- t hrow syst em of error handling is largely a philosophical decision. Bot h have t heir advant ages and disadvant ages and will be m ore or less appropriat e for different sit uat ions. Eit her syst em is bet t er t han having no error handling at all.
Conclusion I n t his chapt er we've covered a lot of ground t hat m ay be unfam iliar t o m any readers. Look closely at t he sam ple applicat ions for t his chapt er and read t he chapt er again if error handling concept s cont inue t o be unclear. One of t he best ways t o discover how an error handler works is t o use debugging t echniques t o single st ep t hrough an applicat ion as it handles an error. We cover debugging t echniques in det ail in Chapt er 16 VBA Debugging. Aft er reading t hat chapt er, you m ight want t o revisit t his chapt er and exam ine t he sam ple applicat ions in m ore det ail using VBA debugging t echniques.
Chapter 13. Programming with Databases A large percent age of nont rivial Excel applicat ions require som e sort of dat a st ore t hat is separat e from t he rest of t he applicat ion, usually in t he form of a dat abase. I n t his chapt er we cover t he basics of dat abase design and dat a access using SQL and ADO. We do not go int o great det ail on any of t hese t opics. All of t hem are book- lengt h subj ect s in t heir own right . Aft er som e brief int roduct ory m at erial, we dem onst rat e t hese t echnologies by showing how t o use t hem for t heir m ost com m on purposes. At t he end of t he chapt er, we suggest addit ional books t hat you can use for in- dept h st udy of t he t opics we int roduce here.
An Introduction to Databases The biggest problem for Excel- cent ric developers m aking t he j um p t o applicat ions based on a backend dat abase is underst anding t he fundam ent al differences bet ween how dat a is t reat ed in Excel and how dat a is t reat ed in a dat abase. A dat abase requires a significant am ount of rigor from t he dat a when com pared t o an Excel worksheet . You can ent er alm ost anyt hing you want on a worksheet , but dat abase t ables are m uch m ore picky. Even som e of t he t hings you ca n ent er in dat abase t ables you shouldn't . Dat abase t ables have a concept of being form ally relat ed t o each ot her, a concept t hat doesn't exist at all in Excel. Modificat ions m ade t o dat abases t ake effect im m ediat ely. There's no need t o " save" t he dat a. Unfort unat ely, t here's also no way t o undo a change m ade t o dat a in a dat abase once t hat change has been com m it t ed. As you can see, working wit h dat abases is significant ly different from working wit h Excel worksheet t ables. This sect ion covers t he m ost im port ant t hings you need t o know about how dat abases work and why you would want t o use one.
N OTE Those of you who are very fam iliar wit h dat abases and t he t erm inology surrounding t hem will not ice t hat we use som e nonst andard t erm s t o describe dat abase concept s in t his sect ion. This is int ent ional and is designed t o explain t hese concept s in t erm s t hat Excel program m ers wit h lim it ed dat abase experience will find easier t o underst and.
Why Use a Database For m any purposes Excel is a perfect ly adequat e dat a cont ainer. However, cert ain com m on circum st ances will force you t o confront a dat abase. These circum st ances include t he following: An e x ist in g da t a ba se The dat a your Excel applicat ion requires m ay already be st ored in a dat abase. I n t hat case, you'll need t o use t he exist ing dat abase, if for no ot her purpose t han t o ext ract t he dat a your applicat ion requires. Ca pa cit y con st r a in t s An Excel worksheet can hold only a lim it ed am ount of dat a, 65,536 rows t o be exact . I t is not uncom m on for a large applicat ion t o be required t o deal wit h m illion s of rows of dat a. You cannot hope t o st ore t his m uch dat a in Excel, so you are forced t o use a dat abase, which can easily m anage t his volum e of dat a. Ope r a t ion a l r e qu ir e m e n t s Excel is not a m ult iuser applicat ion. I t does have som e very unreliable sharing capabilit ies, but as a general rule, if one person is using an Excel workbook
t hat cont ains dat a, anyone else who want s t o use it m ust wait for t hat person t o finish. Dat abases, by cont rast , are inherent ly m ult iuser applicat ions. They are designed from t he ground up t o allow m any people t o access t he sam e dat a at t he sam e t im e. I f your applicat ion requires m ult iple users t o access t he sam e dat a at t he sam e t im e, t hen you probably need a dat abase.
Relational Databases A relat ional dat abase is a set of t ables cont aining dat a organized int o specific cat egories. Each t able cont ains dat a cat egories in colum ns. Each row cont ains a unique inst ance of dat a for t he cat egories defined by t he colum ns. A relat ional dat abase is st ruct ured such t hat dat a can be accessed in m any different ways wit hout having t o reorganize t he t ables. A relat ional dat abase also has t he im port ant advant age of being easy t o ext end. Aft er t he original dat abase has been creat ed, new t ables can be added wit hout requiring all exist ing applicat ions t hat use t he dat abase t o be m odified. The st andard m et hod used t o access relat ional dat a is St ruct ured Query Language ( SQL) , which we describe in t he Dat a Access Techniques sect ion lat er in t he chapt er.
File-Based Databases vs. Client-Server Databases There are t wo broad cat egories of relat ional dat abases: file- based dat abases and client - server dat abases. The fundam ent al difference bet ween t hem has t o do wit h where t he dat a access logic is execut ed. I n a file- based dat abase, t he dat abase consist s of one or m ore files t hat sim ply cont ain t he dat a. When an applicat ion accesses a file- based dat abase, all of t he dat a access logic is execut ed on t he client com put er where t he applicat ion resides. The advant ages of file- based dat abases are t hat t hey are inexpensive, relat ively sim ple and require lit t le ongoing m aint enance. The disadvant ages of file- based dat abases are t hat t hey can creat e significant net work t raffic, t hey are lim it ed in t he am ount of dat a t hey can st ore and lim it ed in t he num ber of sim ult aneous users who can access t hem . Microsoft Access and Microsoft Visual FoxPro are t wo exam ples of file- based dat abases. I n a client - server dat abase, dat abases are cont ained wit hin a larger server applicat ion. This dat abase server is responsible for execut ing t he dat a access request s from client applicat ions. The advant ages and disadvant ages of client server dat abases are m ore or less t he m irror im age of t hose for file- based dat abases. Client server dat abases reduce net work t raffic by handling t he dat a access logic on t he server. They can st ore very large am ount s of dat a and handle very large num bers of sim ult aneous users. However, client - server dat abases are expensive, com plex and require rout ine m aint enance t o keep t hem operat ing efficient ly. Microsoft SQL Server and Oracle are t wo exam ples of client - server dat abases.
Normalization Norm alizat ion is t he process of opt im izing t he dat a in your dat abase t ables. I t s goal is t o elim inat e redundant dat a and ensure t hat only relat ed dat a is st ored in each t able. Norm alizat ion can be t aken t o ext rem e lengt hs. But for m ost developers m ost of t he t im e, underst anding t he first t hree rules of norm alizat ion and ensuring t hat your dat abase is in t hird norm al form is all you'll ever need t o do. ( We cover t he first t hree norm al form s in det ail in t he sect ions t hat follow.)
N OTE Prior t o norm alizing your dat a, you m ust be sure all of t he rows in every t able are unique. A dat abase t able should not cont ain duplicat e rows of dat a and norm alizat ion will not correct t his problem .
Before we can t alk about norm alizat ion, we need t o cover t he concept of a pr im a r y k e y . A prim ary key consist s of one or m ore colum ns in a dat a t able whose value( s) uniquely ident ify each row in t he t able. Let 's t ake a look at a sim ple exam ple. Figure 13- 1 shows a dat abase t able cont aining a list of aut hor inform at ion.
Figu r e 1 3 - 1 . Th e Au t h or s Ta ble
Not ice t hat t here are t wo inst ances of t he nam e Robert in t he First Nam e colum n. These nam es refer t o t wo different people, but if you were t rying t o use only t his colum n t o ident ify rows t here would be no way t o dist inguish t hese from duplicat e ent ries. To uniquely ident ify rows in t his t able, we m ust designat e t he First Nam e a n d Last Nam e colum ns as t he prim ary key. I f you com bine t he values of t hese t wo colum ns t here is no longer any duplicat ion, so all rows are uniquely ident ified. I n t he t ext t hat follows, prim ary key colum ns are oft en referred t o sim ply as k e y colu m n s, whereas any colum ns t hat do not belong t o t he prim ary key are referred t o as n on k e y colu m n s. For our discussion of norm alizat ion, we use t he BillableHours t able shown in Figure 13- 2. This t able cont ains dat a t hat m ight have been ext ract ed from one of our PETRAS t im e- ent ry workbooks. As it st ands, t his dat a is very denorm alized and not suit able for use in a relat ional dat abase. The prim ary key for t his t able consist s of t he com binat ion of t he Consult ant , Dat e, Proj ect and Act ivit y colum ns.
Figu r e 1 3 - 2 . Th e I n it ia l Billa ble H ou r s Ta ble [View full size image]
First Normal Form There are t wo requirem ent s a dat a t able m ust m eet t o sat isfy t he first norm al form :
1 . All colum n values are a t om ic. This m eans t here are no values t hat can be split int o sm aller m eaningful part s. We have one colum n t hat obviously violat es t his requirem ent . The Consult ant colum n consist s of bot h t he first nam e and t he last nam e of each consult ant . This dat a m ust be separat ed int o t wo dist inct colum ns, First Nam e and Last Nam e, t o sat isfy t he first norm al form . 2 . Repeat ing groups of dat a should be elim inat ed by m oving t hem int o new t ables. The Consult ant colum n, even aft er separat ing it int o first nam e and last nam e, violat es t his requirem ent . The solut ion is t o creat e a separat e Consult ant s t able t o hold t his dat a. Each consult ant will be assigned a unique consult ant I D num ber t hat will be used in t he BillableHours t able t o ident ify t he consult ant . The result of t ransform ing our BillableHours t able int o first norm al form is t wo t ablest he m odified BillableHours t able shown in Figure 13- 3 and t he new Consult ant s t able shown in Figure 13- 4. The first colum n in t he BillableHours t able, which previously held each consult ant 's nam e, has been replaced wit h a unique Consult ant I D num ber creat ed in t he new Consult ant s t able.
Figu r e 1 3 - 3 . Th e Billa ble H ou r s Ta ble in Fir st N or m a l For m [View full size image]
Figu r e 1 3 - 4 . Th e N e w Con su lt a n t s Ta ble
This not only allows us t o sat isfy first norm al form , but also allows us t o handle t he sit uat ion in which t wo consult ant s have t he sam e first and last nam es. I n t he original t able t here would have been no way t o dist inguish bet ween t wo consult ant s wit h t he sam e nam e. Before we go any furt her we m ust explain t he concept of a for e ign k e y . A foreign key is a colum n in one t able t hat uniquely ident ifies records in som e ot her t able. I n t he case above, t he Consult ant I D colum n in t he BillableHours t able is a foreign key colum n, each of whose values ident ify a single, unique consult ant in t he new Consult ant s t able. The use of foreign keys is ubiquit ous in relat ional dat abases. As we will see in t he upcom ing sect ion on Relat ionships and Referent ial I nt egrit y , foreign keys are used t o creat e connect ions bet ween relat ed t ables in a dat abase.
Second Normal Form
There are t wo requirem ent s a dat a t able m ust m eet t o sat isfy t he second norm al form :
1 . The t able m ust be in first norm al form . Each successive norm al form builds upon t he previous norm al form . Because our BillableHours t able is already in first norm al form , t his requirem ent has been sat isfied. 2 . Each colum n in t he t able m ust depend on t he whole prim ary key. This m eans if any colum n in t he prim ary key were rem oved, you could no longer uniquely ident ify t he rows in any nonkey colum n in t he t able. The prim ary key in our BillableHours t able consist s of a com binat ion of t he Consult ant I D, Dat e, Proj ect and Act ivit y colum ns. Do we have any colum ns t hat are not dependent on all four of t hese key colum ns? Yes. The Client colum n depends only on t he Proj ect colum n, because a proj ect nam e uniquely ident ifies t he client for whom t he proj ect is com plet ed. To solve t his problem we will rem ove t he client colum n from t he BillableHours t able and creat e a new Client s t able. We will also creat e a new Proj ect s t able t hat provides each proj ect wit h a unique I D num ber, and use t his proj ect I D rat her t han t he proj ect nam e in t he BillableHours t able. This will serve t wo purposes. The new Proj ect s t able will provide a link from t he BillableHours t able t o t he Client s t able ( which we discuss in m ore det ail lat er in t he chapt er) . I t will also enable us t o handle t he sit uat ion in which t wo client s have t he sam e proj ect nam e. The result of t ransform ing our BillableHours t able int o second norm al form is t hree t ables. The m odified BillableHours t able, shown in Figure 13- 5, t he new Client s t able, shown in Figure 13- 6, and t he new Proj ect s t able, shown in Figure 13- 7 ( t his is in addit ion t o t he Consult ant s t able we creat ed in t he previous sect ion) .
Figu r e 1 3 - 5 . Th e Billa ble H ou r s Ta ble in Se con d N or m a l For m [View full size image]
Figu r e 1 3 - 6 . Th e N e w Clie n t s Ta ble
Figu r e 1 3 - 7 . Th e N e w Pr oj e ct s Ta ble
N OTE The sharp- eyed am ong you m ay have not iced t hat t he Rat e colum n in t he BillableHours t able also violat es second norm al form . This is absolut ely correct , so give yourself a gold st ar if you caught t his. However, t his colum n is part of such a good exam ple for dem onst rat ing t hird norm al form t hat we've decided t o post pone it for t hat t opic.
Third Normal Form There are t hree requirem ent s a dat a t able m ust m eet t o sat isfy t hird norm al form :
1 . The t able m ust be in second norm al form . This requirem ent has been m et by t he m odificat ions we m ade in t he previous sect ion.
2 . Nonkey colum ns cannot describe ot her nonkey colum ns. This requirem ent can be m em orably expressed as " Nonkey colum ns m ust represent t he key, t he whole key and not hing but t he key." I n our BillableHours t able, t he Rat e colum n depends only on t he Act ivit y key colum n. All t he ot her key colum ns could be rem oved from t he t able and t he values in t he Rat e colum n could st ill be uniquely associat ed wit h t he rem aining Act ivit y colum n. We can solve t his problem by creat ing a new t able t o hold t he list of Act ivit ies and t heir associat ed rat es. The BillableHours t able will ret ain only an act ivit y I D num ber in place of t he previous Act ivit y and Rat e colum ns. 3 . The t able cannot cont ain de r ive d da t a . Derived dat a refers t o a colum n in a dat a t able whose values have been creat ed by applying a form ula or t ransform at ion t o t he values in one or m ore ot her colum ns in t he t able. I n our BillableHours t able, t he Charge colum n is a derived colum n t hat is t he result of m ult iplying t he Rat e colum n by t he Hours colum n. Colum ns cont aining derived dat a should sim ply be rem oved from t he t able and calculat ed " on t he fly" whenever t heir values are required. The result of t ransform ing our BillableHours t able int o t hird norm al form is t wo t ables: t he m odified BillableHours t able, shown in Figure 13- 8 and t he new Act ivit ies t able shown in Figure 13- 9.
Figu r e 1 3 - 8 . Th e Billa ble H ou r s Ta ble in Th ir d N or m a l For m
Figu r e 1 3 - 9 . Th e N e w Act ivit ie s Ta ble
Not e t hat our t hird norm al form BillableHours t able consist s of a set of prim ary key colum ns, t he Consult ant I D, Dat e, Proj ect I D and Act ivit yI D colum ns, and a nonkey Hours colum n t hat depends on t he ent ire prim ary key and not hing but t he prim ary key. I f any prim ary key colum n were rem oved from t he t able, it would no longer be possible t o uniquely ident ify any of t he ent ries in t he Hours colum n. Because t here are no ot her nonkey colum ns in t he t able, t he Hours colum n cannot possibly depend on any nonkey colum ns. Our dat a is now ready t o be used in a relat ional dat abase.
When Not to Normalize I n t he vast m aj orit y of cases, you will always want t o follow t he norm alizat ion rules out lined above when preparing your dat a for st orage in a relat ional dat abase. As wit h alm ost every ot her rule, however, t here are except ions. The m ost com m on except ion has t o do wit h derived colum ns. When we t ransform ed our BillableHours t able int o t hird norm al form we elim inat ed t he derived colum n t hat showed t he t ot al charge for each line it em . This is generally a good pract ice because if you have derived colum ns you m ust also creat e logic t o ensure t hose colum ns are updat ed correct ly if t he values of any of t he colum ns t hey depend on change. This creat es overhead t hat is best deferred unt il you act ually need t o query t he derived value. I n som e cases, however, it m akes sense t o st ore derived dat a. This is usually t he case when t he dat a is derived from colum ns t hat are very unlikely t o change. I f t he colum ns t hat t he derived dat a depends on are unlikely t o change, t he derived dat a is also unlikely t o change. I n t his case you can im prove t he perform ance of queries t hat access t he derived dat a by calculat ing it in advance and st oring t he result so t he query can sim ply ret rieve it s value rat her t han having t o calculat e it on t he fly.
Relationships and Referential Integrity The abilit y t o t ake advant age of r e la t ion sh ips and r e fe r e n t ia l in t e gr it y are t wo of t he prim ary advant ages t hat relat ional dat abases provide over Excel for dat a st orage. The abilit y t o creat e form al relat ionships bet ween t ables enables you t o avoid m assive repet it ion of dat a and it s associat ed frequency of dat a- ent ry errors t hat lead t o bad or " dirt y" dat a. Referent ial int egrit y enables you t o ensure t hat dat a ent ered in one t able is consist ent wit h dat a ent ered in ot her relat ed t ables. Neit her one of t hese capabilit ies is available t o dat a st ored in Excel.
Foreign Keys Before we can discuss relat ionships and referent ial int egrit y, we need t o have a firm underst anding of foreign keys. A foreign key is a colum n in one t able ( t he r e fe r e n cin g t able) cont aining dat a t hat uniquely ident ifies records from anot her t able ( t he r e fe r e n ce d t able) . The foreign key serves t o connect t he t wo t ables and ensures t hat only valid dat a from t he referenced t able can be ent ered in t he referencing t able. The Act ivit yI D colum n in t he BillableHours t able shown in Figure 13- 8 is a foreign key t hat refers t o t he Act ivit yI D colum n in t he Act ivit ies t able in Figure 13- 9.
For a colum n t o serve as a foreign key, it m ust eit her be t he prim ary key of t he referenced t able or a colum n on which a unique index has been defined. We cover unique indexes lat er in t he chapt er. A foreign key can also consist of m ult iple colum ns, as long as t he const raint s above are followed. Foreign keys provide t he basis of creat ing relat ionships bet ween t ables. I n t his sect ion we will be using t he Microsoft Access Relat ionships window t o visually display t he effect of relat ionships and referent ial int egrit y. Figure 13- 10 shows t he relat ionship described above bet ween t he BillableHours t able and t he Act ivit ies t able. Each t able's prim ary key colum ns are shown in bold.
Figu r e 1 3 - 1 0 . Th e Re la t ion sh ip Be t w e e n t h e Billa ble H ou r s a n d Act ivit ie s Ta ble s
Types of Relationships The vast m aj orit y of relat ionships bet ween dat abase t ables fall int o one of t hree cat egories: one t o one, one t o m any, and m any t o m any.
One to One I n t his t ype of relat ionship, each row in one t able is associat ed wit h a single row in anot her t able. One- t o- one relat ionships are not frequent ly encount ered. The m ost com m on reason for creat ing a one- t o- one relat ionship is t o divide a single t able int o it s m ost frequent ly accessed sect ions and least frequent ly accessed sect ions. This can im prove perform ance by m aking t he m ost frequent ly accessed t able sm aller. All t hings being equal, t he rows in a sm all t able can be accessed m ore quickly t han t he rows in a large t able. A sm aller t able is also m ore likely t o rem ain in m em ory or require less bandwidt h t o t ransfer t o t he client depending on t he t ype of dat abase being used. Consider t he hypot het ical Part s t able shown in Figure 13- 11.
Figu r e 1 3 - 1 1 . Th e Pa r t s Ta ble
Assum e t his t able has a very large num ber of rows, and t he only colum ns you want t o access in m ost cases are t he Part Num ber, Part Nam e, Unit Price and Weight colum ns. You can im prove t he perform ance of queries against your Part s t able by dividing it int o t wo t ables, wit h t he m ost frequent ly accessed colum ns in one t able and t he rest in a second t able. Each row in t hese t wo new t ables will be relat ed t o exact ly one row in t he ot her t able and t he t wo t ables will share a prim ary key. The result of t his part it ioning is shown in Figure 13- 12.
Figu r e 1 3 - 1 2 . Th e Pa r t s Ta ble D ivide d in t o Tw o Ta ble s w it h a On e - t o- On e Re la t ion sh ip
The Access Relat ionship window indicat es a one- t o- one relat ionship by placing t he num ber 1 on each side of t he relat ionship line connect ing t he t wo t ables.
One to Many This is by far t he m ost com m on t ype of relat ionship. I n a one- t o- m any relat ionship, a single row in one t able can be relat ed t o zero, one or m any rows in anot her t able. As Figure 13- 13 shows, every relat ionship creat ed in t he process of norm alizing our BillableHours t able is a one- t o- m any r elat ionship.
Figu r e 1 3 - 1 3 . On e - t o- M a n y Re la t ion sh ips
The Access Relat ionship window indicat es a one- t o- m any relat ionship by placing t he num ber 1 on t he one side of t he relat ionship line and t he infinit y sym bol on t he m any side of t he relat ionship line. Each row in t he Consult ant s, Proj ect s and Act ivit ies t able can be relat ed t o zero, one or m any rows in t he BillableHours t able. The one or m any part of t he relat ionship should be obvious; t he zero part m ay require som e addit ional explanat ion. I f a new consult ant has j oined t he com pany but not yet logged any billable hours, t hat consult ant would be represent ed by a row in t he Consult ant s t able. However, t he row represent ing t he new consult ant would not be associat ed wit h any rows in t he BillableHours t able. The Consult ant s t able would have a one- t o- m any relat ionship wit h t he BillableHours t able, and t he row represent ing t he new consult ant in t he Consult ant s t able would be associat ed wit h zero rows in t he BillableHours t able.
N OTE Som e of you m ay be t hinking t hat Excel's dat a- validat ion list feat ure provides t he sam e benefit as a one- t o- m any dat abase relat ionship. Unfort unat ely, t his is not t rue. Alt hough it 's a very useful feat ure, a dat a- validat ion list only enforces a one- t im e relat ionship
check, at t he t im e an ent ry is select ed from t he list . Unlike a dat abase relat ionship, aft er an ent ry has been select ed from a dat a- validat ion list it does not m aint ain any connect ion t o t he list from which it was select ed.
Many to Many I n t his t ype of relat ionship, each row in one t able can be relat ed t o m ult iple rows in t he ot her t able. This concept can be a bit difficult t o visualize. We dem onst rat e it by ext ending t he exam ple we used in t he Nor m alizat ion sect ion t o include t he role each consult ant plays on each proj ect . Our list of roles m ight look like t he Roles t able shown in Figure 13- 14.
Figu r e 1 3 - 1 4 . Th e Role s Ta ble
Each proj ect will require m ult iple roles in order t o com plet e it , and consult ant s will serve in different roles on different proj ect s. A consult ant m ight be a program m er on one proj ect and a t est er on anot her. On a sm all proj ect , in fact , one consult ant could very well serve m ult iple roles. This m eans t hat role inform at ion cannot be at t ached t o eit her t he Proj ect s t able or t he Consult ant s t able because t here are m any- t o- m any relat ionships bet ween proj ect s and roles as well as consult ant s and roles. Each proj ect requires m ult iple roles and each role applies t o m ult iple proj ect s. Likewise, each consult ant can serve m ult iple roles and each role can be served by m ult iple consult ant s. Most relat ional dat abases cannot direct ly represent a m any- t o- m any relat ionship. When t his t ype of relat ionship is encount ered, however, it can always be broken up int o m ult iple one- t o- m any relat ionships linked by an int erm ediat e t able. I n our Proj ect s, Consult ant s and Roles exam ple, t he m any- t o- m any relat ionships would be represent ed in t he dat abase as shown in Figure 13- 15.
Figu r e 1 3 - 1 5 . M a n y- t o- M a n y Re la t ion sh ips Re solve d in t o On e - t o- M a n y Re la t ion sh ips [View full size image]
The Proj ect Consult ant Role t able serves as t he int erm ediat e t able t hat enables us t o convert t he m any- t o- m any relat ionships bet ween t he Proj ect s and Roles t ables and t he Consult ant s and Roles t ables int o one- t o- m any relat ionships wit h t he Proj ect Consult ant Role t able in t he m iddle. The Proj ect s, Consult ant s and Roles t ables now have one- t o- m any relat ionships wit h t he Proj ect Consult ant Role t able, allowing t he m any- t o- m any relat ionships bet ween t he Proj ect s and Roles t ables and t he Consult ant s and Roles t ables t o be im plem ent ed.
Referential Integrity Aft er your dat abase has been fully norm alized and you have est ablished relat ionships am ong your t ables, referent ial int egrit y ensures t hat t he dat a in t hose t ables rem ains valid when dat a is added, m odified or delet ed. I n Figure 13- 13, for exam ple, referent ial int egrit y ensures t hat invalid rows cannot be added t o t he BillableHours t able by enforcing t he fact t hat t he Consult ant I D, Proj ect I D and Act ivit yI D foreign key colum ns all refer t o valid rows in t heir respect ive source t ables. ( The validit y of t he Dat e and Hours colum ns is ensured by dom ain const raint s t hat can be added t o t hose colum ns. The t opic of dom ain const raint s is beyond t he scope of t his chapt er.)
N OTE All m odern relat ional dat abases support t he concept of referent ial int egrit y, but t he st eps required t o im plem ent it are com plet ely different from one dat abase t o t he next . Consult t he docum ent at ion for your dat abase t o det erm ine how t o im plem ent referent ial int egrit y.
Natural vs. Artificial Primary Keys
There is an ongoing debat e wit hin t he dat abase com m unit y over whet her t he prim ary key for a t able should be nat ural, which is t o say it should consist of one or m ore colum ns t hat nat urally occur in t he t able, or whet her it should be art ificial. An art ificial key is a unique but ot herwise m eaningless num ber t hat is aut om at ically added t o each row in a t able. Most m odern relat ional dat abases provide a special colum n t ype t hat will aut om at ically generat e a unique num ber for every row added t o a t able. To oversim plify t he debat e, nat ural keys t end t o be advocat ed by people who prim arily design dat abases, whereas art ificial keys t end t o be advocat ed by people who m ust live and work wit h dat abases. The argum ent really com es down t o t wo point s: providing easily usable foreign keys and ensuring t he t rue uniqueness of records in a t able. The form al t ask of a prim ary key can be divided int o t wo very sim ilar but subt ly different t asks: uniquely ident ifying each row in a t able and ensuring every row in a t able is unique. The first t ask provides a foreign key t hat can be used by ot her t ables t o uniquely ident ify a specific row in a t able while t he second t ask enforces dat a int egrit y by ensuring t hat you cannot creat e duplicat e records in a t able. When you have a t able wit h a prim ary key t hat is perform ing bot h t asks sim ult aneously, problem s begin t o arise when you need t o reference t hat t able from anot her t able t hrough t he use of a foreign key. Take, for exam ple, our BillableHours t able. This t able requires t he use of four colum ns t o const ruct a nat ural prim ary key. I f you need t o reference t he BillableHours t able from anot her t able in your dat abase, you will need t o add a foreign key t o t hat t able consist ing of all four of t hese colum ns. This is because all four colum ns are required t o uniquely ident ify a record in t he BillableHours t able. As you can im agine, t his can becom e very unwieldy. Assum e we have an I nvoices t able t hat needs t o reference t he BillableHours t able. An exam ple of how t his would be accom plished using a nat ural key is shown in Figure 13- 16.
Figu r e 1 3 - 1 6 . Usin g a N a t u r a l Ke y for t h e Billa ble H ou r s Ta ble
The alt ernat ive is t o separat e t he t wo t asks of t he prim ary key bet ween t wo different const ruct s. The t ask of uniquely ident ifying a row in t he t able can be accom plished wit h a single colum n t hat provides a unique num ber for each row. This colum n becom es t he prim ary key for t he t able. When
you need t o reference t his t able from anot her t able, t he only colum n you will need t o im port as t he foreign key is t he single num eric prim ary key colum n. I n Figure 13- 17 we have added an art ificial prim ary key t o t he BillableHours t able. This is j ust a num ber t hat uniquely ident ifies each row in t he t able. See how m uch sim pler t his m akes t he t ask of referencing a record in t he BillableHours t able from t he I nvoices t able.
Figu r e 1 3 - 1 7 . Usin g a n Ar t ificia l Ke y for t h e Billa ble H ou r s Ta ble
I f all we did was creat e an art ificial prim ary key consist ing of an aut om at ically generat ed unique value t hat had no real m eaning, you could easily ent er t wo com plet ely ident ical rows in t he BillableHours t able and t he dat abase would m ake t hem art ificially unique by creat ing a unique value in t he art ificial prim ary key colum n. The t ask of ensuring dat a uniqueness can be accom plished wit h a unique index. A unique index is a special const ruct you can creat e t hat includes one or m ore colum ns in a t able. The values in t he colum n( s) on which t he unique index is defined m ust be unique. The index will not allow duplicat e values t o be ent ered. I n t he case of our BillableHours t able, a unique index would include t he Consult ant I D, Dat e, Proj ect I D and Act ivit yI D colum ns. Not e t hat t hese colum ns are t he colum ns t hat originally form ed t he nat ural prim ary key. At t he risk of ant agonizing t hose on t he opposit e side of t his debat e, we recom m end t he use of art ificial prim ary keys. Art ificial prim ary keys m ake dat abase const ruct ion and use m uch sim pler in pract ice, and you can easily enforce t rue uniqueness in every t able by creat ing a unique index on t he colum ns t hat would ot herwise form t he nat ural prim ary key.
N OTE All m odern relat ional dat abases support t he creat ion of unique indexes, but t he m et hod used t o creat e t hem is com plet ely different from one dat abase t o t he next . Consult t he docum ent at ion for your dat abase t o det erm ine how t o creat e unique indexes on a t able.
Designing the Data Access Tier As you saw in Chapt er 3 Excel and VBA Developm ent Best Pract ices, t he dat a access code in an applicat ion form s a unique logical t ier t hat should be separat ed from t he ot her applicat ion t iers. The m ain reason for t his is over t he lifet im e of a nont rivial Excel applicat ion, t he dat a- st orage m echanism is very likely t o change. For exam ple, when dat a- st orage requirem ent s are sim ple, dat a can be st ored in an Excel workbook. As t he am ount of dat a and t he num ber of users who need t o access it grows, a file- based dat abase m ay be required. Som e applicat ions will grow so large and at t ract so m any users t hat t heir dat a access needs can only be m et by a client - server dat abase. Your applicat ion should be designed so changes in t he dat a access t ier have a m inim al im pact on t he rest of t he applicat ion when m oving from one dat a st orage m echanism t o anot her. Applicat ion t iers ot her t han t he dat a access t ier should have no knowledge or im plicit reliance on any part icular dat a st orage m echanism . I t is t he j ob of t he dat a access t ier t o abst ract away t he part icular dat a st orage m echanism used and present all dat a t o t he business logic t ier in t he sam e form at , regardless of whet her t he dat a com es from a local Excel workbook or a rem ot e SQL Server dat abase. I n an ideal design, t he public m et hods of t he dat a access t ier will be driven by t he needs of t he business logic t ier. The business logic t ier should be able t o call t he dat a access t ier t o ret rieve, input , updat e or delet e dat a in a m anner t hat reflect s t he applicat ion logic. The applicat ion logic will be t ranslat ed int o t he specific act ions required t o accom plish t he physical t ask on what ever dat a st orage m echanism is in use. I f possible, all dat a should be t ransferred bet ween t he business logic t ier and t he dat a access t ier by way of user- defined t ypes. Use ADO recordset s if absolut ely necessary, but be aware t hat t he use of recordset s int roduces som e undesired linkages bet ween business logic and dat a access t iers, in t he form of t he physical ordering of t he dat a in t he recordset , for exam ple. ADO will be described in m ore det ail in t he next sect ion.
Data Access with SQL and ADO An Introduction to ActiveX Data Objects (ADO) ADO is t he dat a access t echnology designed by Microsoft t o m ake dat a access as sim ple and t ransparent as possible, regardless of how or where t he dat a is st ored. Previous dat a access t echnologies, m ost not ably DAO and ODBC, are st ill in use. However, t hese t echnologies have been officially deprecat ed, m eaning t hat Microsoft will no longer enhance t hem and m ay drop support for t hem at any t im e. For several years, as of t his writ ing, ADO has been t he sole designat ed dat a access t echnology for now and t he fut ure.
Data Access Technology Defined A dat a access t echnology such as ADO can be t hought of as a connect or bet ween your applicat ion and t he dat a- st orage m echanism it uses. ADO allows your applicat ion t o " t alk" t o dat abases in t he VBA program m ing language. You do st ill need t o underst and t he use of SQL, which will be discussed t hroughout t he rest of t his chapt er, but you do not need t o underst and t he low- level API s required t o t ranslat e SQL- based request s bet ween VBA and t he t arget dat a- st orage applicat ion. ADO abst ract s t hese low- level requirem ent s int o a set of com m on obj ect s, propert ies and m et hods t hat are used in t he sam e way no m at t er what dat a- st orage applicat ion you're working wit h.
N OTE For sim ple applicat ions where t he dat a is st ored ent irely wit hin open Excel workbooks, you will rarely need t o use a separat e dat a access t echnology such as ADO. VBA will do t he j ob quit e nicely. However, your dat a access code should st ill be separat ed int o it s own logical t ier in order t o m inim ize t he num ber of problem s you'll encount er if you need t o m ove t o a dat a st orage m echanism t hat does require t he use of ADO.
How does ADO accom plish t his? I t operat es t hrough a lower- level t echnology called OLE DB. You can t hink of OLE DB as ADO for C+ + program m ers. OLE DB cannot be used direct ly from VBA, but ADO t ranslat es t he OLE DB dat a access m echanism s int o a form VBA underst ands. The root of OLE DB's abilit y t o m ake all dat a st orage applicat ions look t he sam e is a com ponent called a pr ovide r . There is an OLE DB provider for each dat a- st orage applicat ion. This provider t ranslat es t he unique, low- level API s of t hat applicat ion int o a com m on OLE DB int erface. ( The concept of int erfaces is covered ext ensively in Chapt er 11 I nt erfaces.) Providers for t he m ost com m only used dat abases are packaged wit h ADO.
I f you need t o use ADO t o access a dat abase for which a provider has not been supplied, t here are t wo pot ent ial opt ions. You can obt ain a nat ive OLE DB provider for t hat dat abase from a t hird- part y vendor if one is available. I f no t hird- part y provider is available you can fall back on t he OLE DB provider for ODBC. ODBC was an early indust ry st andard low- level dat a access t echnology pioneered by Microsoft . Any dat abase applicat ion t hat support s ODBC can be accessed by ADO using t he OLE DB provider for ODBC. We discuss t he t opic of providers from an ADO point of view in m ore det ail lat er in t his sect ion.
ADO Objects ADO is built on a very sim ple yet ext rem ely flexible obj ect m odel. For t he vast m aj orit y of purposes you will require only t hree t op- level obj ect s: Connect ion, Com m and and Recordset , and t hree collect ions t hat belong t o t hese t op- level obj ect s: Errors, Param et ers and Fields. The t op- level obj ect s are not organized hierarchically. Rat her t hey can each be creat ed and used along wit h or independent ly of t he ot hers. As shown in Figure 13- 18, t he core ADO obj ect m odel can be visualized as a t riangle am ong t he t hree t op- level obj ect s wit h t he collect ions at t ached t o t he t oplevel obj ect t hey belong t o.
Figu r e 1 3 - 1 8 . Th e Cor e AD O Obj e ct M ode l
A brief descript ion of t he general purpose of each obj ect and collect ion is provided below. We show specific code exam ples t hat use each of t hese obj ect s and collect ions lat er in t he chapt er.
Top-Level Objects Con n e ct ion The prim ary purpose of t he Connect ion obj ect is exact ly what it s nam e im plies:
connect ing t o a dat abase. I n keeping wit h t he flexibilit y of ADO, however, a Connect ion obj ect can be used t o execut e SQL st at em ent s or st ored procedures direct ly. Com m a n d The Com m and obj ect is t ypically used t o execut e SQL st at em ent s or st ored procedures t hat do not ret urn dat a and/ or require param et ers. Re cor dse t The Recordset obj ect is used t o execut e SQL st at em ent s or st ored procedures t hat will ret urn dat a t o your applicat ion.
Collections Er r or s The Errors collect ion is used by ADO t o ret urn any errors generat ed by t he provider used t o connect t o t he dat a source. These errors are not ADO errors. ADO obj ect s generat e runt im e errors. Pa r a m e t e r s The Param et ers collect ion is used t o st ore t he variable argum ent s t hat will be passed t o a SQL st at em ent or st ored procedure. This allows you t o const ruct a reusable Com m and obj ect cont aining all t he st at ic inform at ion t o which you sim ply pass different param et ers for each call. Fie lds The Fields collect ion is used t o access t he dat a cont ained in a Recordset obj ect . A recordset can be t hought of as cont aining a t able in which only one row is act ive at a t im e. The Fields collect ion exposes t he values in t he colum ns corresponding t o t he act ive row.
Connecting to Data Sources Before you can do anyt hing wit h t he dat a in a dat a source you m ust est ablish a connect ion t o t he dat a source. As wit h m ost t asks involving ADO, t here is m ore t han one way t o do t his. A connect ion can be specified by set t ing individual propert ies of an ADO Connect ion obj ect or by grouping all t he required propert ies int o a con n e ct ion st r in g. We will dem onst rat e how t o est ablish connect ions using connect ion st rings. A connect ion st ring t ells ADO what provider t o use, what dat a source t o connect t o and any ot her det ails required t o est ablish t he connect ion ( a user nam e and password, for exam ple) . A connect ion st ring consist s of a series of propert y- value pairs, each followed by a sem icolon. The first propert y specified in your connect ion st ring should be t he Pr ovide r propert y. This t ells ADO what provider you want t o use. The provider det erm ines t he st ruct ure of t he rest of t he connect ion st ring. Each provider has a unique set of addit ional propert ies, som e of which are required and som e of which are opt ional. I f no provider is specified, t he default provider is t he OLE DB provider for ODBC. This is for backward com pat ibilit y wit h old- st yle ODBC connect ion st rings. This is rarely t he provider you want t o use, so always specify t he provider in your connect ion st rings. Sam ple connect ion st rings for several com m on dat a sources are shown below.
Access
The OLE DB Provider for Microsoft Jet is used t o connect t o Access dat abases. The synt ax for a t ypical connect ion st ring t o an Access dat abase is shown in List ing 13- 1.
List in g 1 3 - 1 . An Acce ss D a t a ba se Con n e ct ion St r in g Dim sConnect As String sConnect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Files\MyDatabase.mdb;" & _ "User ID=UserName;" & _ "Password=Password;"
As you can see, t he first argum ent specifies t he provider you want t o use. Aft er you have specified t he OLE DB Provider for Microsoft Jet , t he only ot her propert y t hat is always required is t he Dat a Source propert y. This t ells OLE DB where t he Access dat abase you want t o connect t o is locat ed. I f your Access dat abase is password- prot ect ed, you can pass it t he user nam e and password required t o open it using t he User I D and Password propert ies.
N OTE Microsoft has deprecat ed t he OLE DB Provider for Microsoft Jet . This m eans t hat it st ill works but is no longer act ively dist ribut ed and t hat support for it will end at som e point in t he fut ure. ADO version 2.5 was t he last version of ADO wit h which t his provider was dist ribut ed. I f you or your users do not have t his version of ADO inst alled, you can download it from t he Microsoft Web sit e at ht t p: / / m sdn.m icr osoft .com / dow nloads/ .
SQL Server The OLE DB Provider for SQL Server is used t o connect t o SQL Server dat abases. The base connect ion st ring synt ax for t his provider is shown in List ing 13- 2.
List in g 1 3 - 2 . Ba se Con n e ct ion St r in g Syn t a x for SQL Se r ve r Dim sConnect As String sConnect = "Provider=SQLOLEDB;" & _ "Data Source=Server\Instance;" & _ "Initial Catalog=DatabaseName;"
The D a t a Sou r ce propert y specifies t he server and inst ance you want t o connect t o. SQL Server 2000 and lat er can have m ult iple server inst ances, each cont aining m ult iple dat abases. I n prior versions of SQL Server, you would j ust specify t he server nam e. I n eit her case, t he server nam e is t ypically t he sam e as t he nam e of t he com put er on which SQL Server is inst alled. The I n it ia l Ca t a log propert y is used t o specify t he dat abase you want t o connect t o. There are t wo com m on synt ax variat ions used when connect ing t o a SQL Server dat abase. The variat ion you need t o use depends on how t he login securit y has been configured on t he SQL Server you are connect ing t o. Unlike Access, SQL Server always requires som e sort of login credent ials. The t wo t ypes of SQL Server securit y are st a n da r d se cu r it y and W in dow s in t e gr a t e d se cu r it y. St andard securit y m eans t hat you log in t o SQL Server wit h a user nam e and password specific t o SQL Server. I nt egrat ed securit y m eans your Windows net work login credent ials are used t o validat e your connect ion t o SQL Server. The connect ion st ring used for st andard securit y is shown in List ing 13- 3 . The connect ion st ring used for int egrat ed securit y is shown in List ing 13- 4.
List in g 1 3 - 3 . Con n e ct ion St r in g for SQL Se r ve r w it h St a n da r d Se cu r it y Dim sConnect As String sConnect = "Provider=SQLOLEDB;" & _ "Data Source=ServerName\InstanceName;" & _ "Initial Catalog=DatabaseName;" & _ "User ID=UserName;" & _ "Password=Password;"
List in g 1 3 - 4 . Con n e ct ion St r in g for SQL Se r ve r w it h I n t e gr a t e d Se cu r it y Dim sConnect As String sConnect = "Provider=SQLOLEDB;" & _ "Data Source=ServerName\InstanceName;" & _ "Initial Catalog=DatabaseName;" & _ "Integrated Security=SSPI"
Excel You can connect t o an Excel workbook using eit her t he OLE DB Provider for ODBC or t he OLE DB Provider for Microsoft Jet . We dem onst rat e t he use of t he lat t er. The synt ax for t his is shown in List ing 13- 5.
List in g 1 3 - 5 . Con n e ct ion St r in g for a n Ex ce l W or k book Dim sConnect As String sConnect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Files\MyWorkbook.xls;" & _ "Extended Properties=""Excel 8.0;HDR=YES"";"
Not e t he Ex t e n de d Pr ope r t ie s propert y. This propert y consist s of t wo values. The first t ells t he Jet provider you are connect ing t o an Excel workbook. The second t ells t he provider how t o int erpret t he dat a in t he sheet or range you specify in your SQL st at em ent s. A value of HDR=YES indicat es t he first row of dat a cont ains t he colum n nam es. I f t his value is om it t ed or if it is specified as HDR=NO, t he provider will assum e all values in t he t able are dat a and t here are no colum n nam es.
Using the Connection String Aft er you have built your connect ion st ring, you can assign it t o t he Connect ionSt ring propert y of t he ADO Connect ion obj ect or pass it direct ly t o various propert ies and m et hods of ot her t op- level ADO obj ect s. List ing 13- 6 dem onst rat es how t o open a connect ion t o an Access dat abase using t he Connect ion obj ect .
N OTE To use ADO from your Excel VBA proj ect , you m ust set a reference t o t he Microsoft Act iveX Dat a Obj ect s 2.x library, where x represent s t he highest num bered library available on your syst em .
List in g 1 3 - 6 . Ope n in g a Con n e ct ion t o a n Acce ss D a t a ba se Public gcnAccess As ADODB.Connection Sub OpenAccessConnection() Dim sConnect As String sConnect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=E:\MyDatabase.mdb;" Set gcnAccess = New ADODB.Connection gcnAccess.ConnectionString = sConnect gcnAccess.Open
' Code to use the connection goes here. gcnAccess.Close End Sub
Connect ing t o ot her dat a sources works exact ly t he sam e way. We show ot her ways t hat t he connect ion st ring can be used in lat er sect ions. Not e t hat our ADO Connect ion obj ect was declared as a global variable. This is required t o enable a very im port ant ADO feat ure called con n e ct ion poolin g . Creat ing and t earing down a connect ion t o a dat abase is a resource- int ensive process. I f your applicat ion will be perform ing a large num ber of dat abase operat ions, you do not want ADO t o have t o creat e and dest roy a connect ion for each one of t hem . To enable connect ion pooling, you m ust do t he following: Declare your ADO Connect ion obj ect as a global or m odule- level variable. Creat e your ADO connect ion on applicat ion st art up and t hen close it . Each t im e a procedure needs t o use t he connect ion it should open t he connect ion, use it and t hen close it . Do not set t he Connect ion obj ect t o Not hing unt il your applicat ion is shut t ing down. By following t hese procedures you will allow ADO ( or m ore accurat ely, t he underlying OLE DB provider) t o hold t he dat abase connect ion open behind t he scenes and provide your procedures wit h an exist ing connect ion on request rat her t han forcing it t o creat e a new connect ion each t im e one is needed.
Error Handling Connections When t alking t heoret ically about m aking dat abase connect ions, t he connect ion at t em pt s always succeed and you cont inue along your m erry way. I n t he real world, t his is not always t he case. When you are at t em pt ing t o access a dat abase t hat is not locat ed on t he sam e com put er as your code, any num ber of t hings can cause t he connect ion at t em pt t o fail. The solut ion t o t his problem is t o use your error handler t o look for t he error t hat is generat ed when a connect ion at t em pt fails and t hen handle it appropriat ely. Because congest ed net work condit ions or very busy dat abases can bot h cause t ransient connect ion problem s, t he best st rat egy is t o ret ry t he connect ion at t em pt a few t im es before giving up and inform ing t he user t hat a connect ion t o t he dat abase could not be est ablished. The code required t o accom plish t his for an at t em pt ed connect ion t o a SQL Server dat abase is shown in List ing 13- 7. For general det ails on t he error handling t echnique used here, refer back t o Chapt er 12 VBA Error Handling .
List in g 1 3 - 7 . Er r or H a n dlin g a Con n e ct ion At t e m pt Public gcnConnect As ADODB.Connection Public Sub ConnectToDatabase() Const sSOURCE As String = "ConnectToDatabase" Dim lAttempt As Long Dim sConnect As String On Error GoTo ErrorHandler ' Create the connection string. sConnect = "Provider=SQLOLEDB;" & _ "Data Source=ServerName\InstanceName;" & _ "Initial Catalog=DatabaseName;" & _ "Integrated Security=SSPI" ' Attempt to open the connection. Application.StatusBar = "Attempting to connect..." Set gcnConnect = New ADODB.Connection gcnConnect.ConnectionString = sConnect gcnConnect.Open ' Close connection to enable connection pooling. gcnConnect.Close ErrorExit: Application.StatusBar = False Exit Sub ErrorHandler: ' We will try to make the connection three times before ' bailing out. If lAttempt < 3 And gcnConnect.Errors.Count > 0 Then If gcnConnect.Errors(0).NativeError = 17 Then Application.StatusBar = "Retrying connection..." lAttempt = lAttempt + 1 Resume End If End If If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then Stop Resume Else Resume ErrorExit End If
End Sub
The error indicat ing a connect ion failure is a provider error, so we m ust exam ine t he Connect ion obj ect 's Errors collect ion in order t o det erm ine whet her a connect ion at t em pt failed. The Errors collect ion m ay cont ain m ult iple errors, but t he first it em in t he collect ion is alm ost always t he one t hat describes t he root cause of t he error. Unlike m ost collect ions t hat you'll encount er in VBA, t he Connect ion obj ect Errors collect ion is indexed beginning wit h zero. The process of ret rying connect ion at t em pt s can be lengt hy. Therefore we've added st at us bar m essages t o keep our users updat ed. Ot herwise t hey m ay t hink t he applicat ion is frozen and at t em pt t o end it wit h Ct rl+ Alt + Del.
Data Access Techniques There are four fundam ent al da t a m a n ipu la t ion operat ions you can perform on t he dat a in a dat a source. You can ret rieve exist ing dat a, you can add new dat a, you can m odify exist ing dat a and you can delet e exist ing dat a. I n t his sect ion we dem onst rat e how t o perform t hese operat ions using SQL and ADO. Any dat a- m anipulat ion operat ion can be perform ed on any dat a source by passing plain- t ext SQL st at em ent s direct ly t o t he dat a source using ADO. Relat ional dat abases also allow you t o st ore prefabricat ed SQL st at em ent s, known as st ored queries or st ored procedures. When working wit h client - server dat abases especially, if you have t he opt ion you should always prefer st ored procedures. They are far m ore efficient t han plain- t ext SQL st at em ent s and you can creat e far m ore com plex dat a access logic wit hin t hem . For t he plain- t ext SQL st at em ent s we dem onst rat e below we will show only t he m ost com m on synt ax elem ent s. Act ual SQL st at em ent s can be m uch m ore com plex and will have dat abasespecific ext ensions available t hat are only valid when you are working wit h t hat dat abase. To keep t his sect ion focused, all t he exam ples shown use Access as t he dat a source. The nice t hing about ADO is t hat it s usage is alm ost ident ical regardless of t he dat a source you are working wit h. However, please see our previous book, t he Excel 2002 VBA Program m er's Reference, for det ailed exam ples of dat a access using SQL Server, Excel and even t ext files.
Retrieving Data I n t his sect ion we dem onst rat e how t o ret rieve dat a using SQL and ADO. Excel also provides a num ber of aut om at ed feat ures for ret rieving and analyzing dat a. These will be covered in Chapt er 14 Dat a Manipulat ion Techniques. The SQL SELECT st at em ent is used t o accom plish dat a ret rieval. The basic synt ax of t he SELECT st at em ent is shown in List ing 13- 8.
List in g 1 3 - 8 . Th e SQL SELECT St a t e m e n t SELECT
FROM