فصل ۱۵ — اشیای اصلی
در فصل قبل، مدلهای شیء را از نظر نحوهٔ کار، Hierarchy و ارسال Parameterها بررسی کردیم. در این فصل Collectionها و Objectهای اصلیای را که در کد برای ارتباط با Application و Database استفاده میشوند با جزئیات بیشتری میبینیم. Objectهای اصلی این فصل عبارتاند از Application، Me، CurrentDb و Recordset.
Application Object
Application در بالاترین نقطهٔ Hierarchy مدل شیء قرار دارد و کل برنامهٔ Access را نمایندگی میکند. این Object همهٔ Objectهای برنامهٔ فعال Access، از جمله DAOها، را در بر میگیرد. همچنین Functionهای مفیدی برای بازگرداندن داده از Tableها و Queryها دارد که رفتاری شبیه Filterها ایجاد میکنند. از Application میتوان برای Quit کردن Access و بستن کامل برنامه نیز استفاده کرد.
Application Object پیشفرض است و لازم نیست همیشه صریحاً در Syntax نوشته شود. برای نمونه، برای بهدستآوردن نام نخستین Printer در Collectionِ Printers:
Printers(0).DeviceName
CurrentDb
CurrentDb یکی از Objectهای سطح بالای DAO یا Data Access Objects است. با آن میتوان Recordset ساخت و با کد VBA با Recordsetها تعامل کرد. به دلیل اهمیت زیاد، در ادامهٔ همین فصل با جزئیات بیشتری بررسی میشود.
CurrentProject
CurrentProject پروژهٔ واقعی Access را نمایندگی میکند و جزئیات تمام Formها، Reportها، Macroها و Moduleها را در Collectionهای زیر نگه میدارد:
AllFormsAllReportsAllMacrosAllModules
این Collectionها بسیار مفیدند، زیرا دربارهٔ تمام Formها و Reportهای Project اطلاعات میدهند، نه فقط Objectهایی که همان لحظه Load شدهاند. برای مقایسه، Collectionِ Forms در Application فقط Formهای باز را شامل میشود.
MsgBox CurrentProject.AllForms("MyForm").DateCreated
این کد Date و Time ایجاد Form با نام MyForm را نمایش میدهد. فصل ۳۲ کاربرد بیشتر این Collectionها را در ساخت Object Inventory نشان میدهد.
Property بسیار مفید دیگر Path است:
MsgBox CurrentProject.Path
هنگام توزیع برنامهٔ Access، کاربران ممکن است برنامه را از Pathهای کاملاً متفاوتی Load کنند؛ بهخصوص اگر Network Driveها به شکل متفاوت Map شده باشند. ممکن است برنامه لازم باشد با فایلهای دیگری در Folder خود مانند Text یا Excel برای Import/Export کار کند. شما از قبل Path دقیق محل نصب کاربر را نمیدانید، اما CurrentProject.Path آن را در Runtime ارائه میکند. این راهکار هنگام جابهجایی Application از Server آزمایشی به Server عملیاتی نیز مفید است؛ Hard-code کردن Pathها نیازمند تغییر کد و مستعد Error است.
DAvg
Functionِ DAvg میانگین یک Numeric Field را از Table مشخص برمیگرداند:
MsgBox DAvg("MyField", "MyTable")
MyField باید Numeric باشد. Criterion اختیاری نیز ممکن است:
MsgBox DAvg("MyField", "MyTable","MyName='Richard'")
داخل Stringِ Criteria، Single Quote مشخص میکند MyName با مقدار String مقایسه میشود.
DCount
DCount تعداد Recordها را از Table مشخص میشمارد:
MsgBox DCount("MyField", "MyTable")
با Criterion:
MsgBox DCount("MyField", "MyTable","MyName='Richard'")
DbEngine و Transaction
DbEngine Object سطحبالای DAO است و برای کار با Transactionها استفاده میشود. هنگام پردازش داده ممکن است Error، ناسازگاری Data Type یا حتی قطع برق باعث توقف Process شود؛ در نتیجه بعضی Recordها پردازش شدهاند و بعضی نه، و معلوم نیست نقطهٔ توقف کجا بوده است. برای Process بزرگ بهتر است Transaction به کار رود تا تغییرها تا Commit نهایی در Table تثبیت نشوند.
Sub EditData()
Dim RecSet As Recordset, WrkSp as Workspace
Set RecSet = CurrentDb.OpenRecordset("Employees")
Set WrkSp = DBEngine.Workspaces(0)
WrkSp.BeginTrans
Do Until RecSet.EOF
If RecSet![First Name] = "Nancy" Then
RecSet.Edit
RecSet![Company] = "Northwind"
RecSet.Update
End If
RecSet.MoveNext
Loop
If MsgBox("Save all changes?", vbQuestion + vbYesNo) = vbYes Then
WrkSp.CommitTrans
Else
WrkSp.Rollback
End If
RecSet.Close
WrkSp.close
Set RecSet = Nothing
Set WrkSp = Nothing
End Sub
Workspace با نام WrkSp به Workspace جاری اشاره میکند. BeginTrans آغاز Transaction را مشخص میکند. پس از پایان Loop، Message Box دربارهٔ ذخیرهٔ همهٔ تغییرها میپرسد؛ Yes باعث CommitTrans و No باعث Rollback میشود.
DFirst
DFirst مقدار Field را از نخستین Record یافتهشده در Table برمیگرداند:
MsgBox DFirst("MyField", "MyTable")
MsgBox DFirst("MyField", "MyTable", "MyValue>6")
DLast
DLast مقدار Field را از آخرین Record یافتهشده برمیگرداند:
MsgBox DLast("MyField", "MyTable")
MsgBox DLast("MyField", "MyTable", "MyValue<=10")
DLookup
DLookup با استفاده از Criteria String مقدار یک Field را پیدا میکند:
MsgBox DLookup("MyField", "MyTable", "MyValue=5")
در این مثال مقدار MyField از Recordی برگردانده میشود که MyValue برابر 5 است. Criteria اختیاری است و در صورت حذف، رفتار شبیه DFirst خواهد بود. DLookup فقط یک مقدار برمیگرداند؛ اگر چند Record با Criteria تطبیق داشته باشند فقط مقدار نخستین مورد برگردانده میشود.
DMax و DMin
DMax بیشترین و DMin کمترین مقدار Field را پیدا میکنند. Field میتواند Data Typeهای مختلف داشته باشد:
MsgBox DMax("MyField", "MyTable")
MsgBox DMax("MyField", "MyTable", "MyValue>6")
MsgBox DMin("MyField", "MyTable")
MsgBox DMin("MyField", "MyTable", "MyValue>6")
DoCmd
DoCmd برای برنامهنویس Access VBA بسیار مهم است، زیرا Commandهای Macro موجود در Macro Window را شبیهسازی میکند. فصل ۱۶ بهطور مفصل به آن اختصاص دارد.
DSum
DSum مقدارهای Numeric یک Field را در Table جمع میکند:
MsgBox DSum("MyField", "MyTable")
MsgBox DSum("MyField", "MyTable", "MyName='Richard'")
MyField باید Numeric باشد و در Criteria متنی، Single Quote درون String استفاده میشود.
DVar و DVarP
DVar و DVarP برای برآورد Variance مقدارهای Field در Table هستند:
MsgBox DVar("MyField", "MyTable")
MsgBox DVar("MyField", "MyTable", "MyName='Richard'")
Field باید Numeric باشد. DVar برای برآورد Variance روی نمونهای از Population و DVarP برای برآورد Variance کل Population به کار میرود.
Forms
Collectionِ Forms تمام Formهای باز و Loadشدهٔ Application را نمایندگی میکند و امکان دستکاری گسترده با Propertyها و Methodها را میدهد. با Collectionِ Controls در هر Form نیز به Property و Methodهای Controlهای منفرد دسترسی دارید.
For Each frm In Forms
MsgBox frm.Name
Next frm
این کد نام هر Formِ Loadشده را نمایش میدهد و Formهای بسته را شامل نمیشود. دسترسی به Control:
MsgBox Forms("MyForm").Controls("label0").Caption
Captionِ Control با نام label0 نمایش داده میشود و همین Property را میتوان تغییر داد.
Printer و Printers
Printer به Propertyهای Printer پیشفرض دسترسی میدهد:
MsgBox Printer.DeviceName
Printers Collection تمام Printerهای در دسترس Application است:
Dim Prt As Printer
For Each Prt In Printers
MsgBox Prt.DeviceName
Next Prt
خروجی نام تمام Print Deviceهاست و با فهرست Printer در Dialog استاندارد Print در Microsoft Office مطابقت دارد.
Quit
Quit همانطور که از نامش پیداست Application را میبندد. استفاده از آن خطرناک است، زیرا Warning نمیدهد و موارد ذخیرهنشده الزاماً Save نمیشوند. DoCmd.Quit نیز قابل استفاده است و Warningهای لازم را ارائه میکند.
References
Collectionِ References تمام Referenceهای External Object Library مورد استفاده در VBA را نگه میدارد:
Dim Reff As Reference
For Each Reff In References
MsgBox Reff.Name
Next Reff
Reports
Collectionِ Reports بسیار شبیه Forms است و فقط Reportهای باز و Loadشده را شامل میشود، اما به Controlهای داخل Report نیز دسترسی میدهد:
Dim Rep As Report
For Each Rep In Reports
MsgBox Rep.Name
Next Rep
برای دسترسی به Control نیز الگوی Collection/Controls به کار میرود؛ متن منبع نمونهٔ زیر را نشان میدهد:
MsgBox Forms("MyForm").Controls("label0").Caption
هدف مثال، خواندن یا تغییر Caption یک Control است.
Screen
Objectِ Screen Propertyهای مربوط به Screen برنامه را فراهم میکند و از جمله میتوان Active Form یا Active Report را تشخیص داد:
MsgBox Screen.ActiveReport.Name
همچنین Mouse Pointer قابل تغییر است:
Screen.MousePointer = 11
Screen.MousePointer = 0
مقدار 11 Cursor را به Hourglass/Busy و مقدار 0 آن را به حالت Default برمیگرداند. DoCmd.Hourglass نیز میتواند کار مشابهی انجام دهد، ولی Screen.MousePointer امکان استفاده از Valueهای متعدد برای Iconهای مختلف را فراهم میکند.
SysCmd و Progress Meter
یکی از کاربردیترین قابلیتهای SysCmd نمایش Progress Meter شبیه Meter اجرای Query است. ابتدا Meter را Initialize کنید:
SysCmd acSysCmdInitMeter, "Making Progress...", 100
Label و Upper Limit برابر 100 تعیین میشود. سپس Meter را Update کنید:
SysCmd acSysCmdUpdateMeter, 10
با Upper Limit 100، مقدار 10 یعنی 10 درصد Progress. در پایان:
SysCmd acSysCmdRemoveMeter
این قابلیت برای Process طولانی شامل چند Query بزرگ مفید است، زیرا بهجای نمایش Progress مستقل هر Query، یک وضعیت کلی به کاربر میدهد.
Version
Propertyِ Version نسخهٔ Access در حال اجرا را مشخص میکند. این موضوع زمانی مهم است که Application روی نسخهای قدیمیتر Load شود ولی کد از Featureهای نسخهٔ جدیدتر استفاده کرده باشد. میتوان Warning داد یا کاربر را مجبور به Quit کرد:
If Application.Version <> "12.0" Then
MsgBox "You need a later version of Access to run this application", vbCritical
Quit
End If
Me Object
Me Object ویژهای برای Form و Report است. چون Form یا Report فعال را نمایندگی میکند، فقط در Moduleهای Form/Report قابل استفاده است، نه در Module معمولی Insertشده. میتوان بهجای آن Collectionِ Forms یا Reports را نوشت:
Forms("MyForm")
اما در Code Module خود Form یا Report، استفاده از Me سادهتر است.
ActiveControl
ActiveControl Control فعال روی Form یا Report را مشخص میکند. در Eventهای Form Load یا Form Activate قابل اتکا نیست، زیرا در آن لحظه ممکن است هنوز Control فعالی وجود نداشته باشد:
Private Sub Detail_Click()
MsgBox Me.ActiveControl.Name
End Sub
نام Control دارای Focus را برمیگرداند. اگر Control از نوع List Box یا Drop-down باشد، میتوان Item انتخابشده را بررسی کرد. نمونهٔ زیر بررسی میکند آیا نخستین Item انتخاب شده است یا نه؛ True برابر -1 و False برابر 0:
MsgBox Me.ActiveControl.Selected(0)
AllowAdditions، AllowDeletions، AllowEdits و AllowFilters
روی Form میتوان اجازهٔ افزودن، حذف و ویرایش Record را کنترل کرد و روی Form یا Report استفاده از Filter را مجاز یا غیرمجاز ساخت:
Me.AllowAdditions = True
Me.AllowDeletions = False
Me.AllowEdits = True
Me.AllowFilters = True
Caption
Me.Caption = "MyCaption"
Captionِ Form یا Report را تعیین میکند.
Dirty
MsgBox Me.Dirty
در Form مشخص میکند دادهٔ جاری تغییر کرده است یا نه؛ True یعنی تغییر انجام شده و False یعنی تغییری وجود ندارد. Form باید به Table یا Query Bound باشد.
Filter
MsgBox Me.Filter
Me.Filter = "MyField>4"
میتوان Filter جاری را خواند یا Filter جدید تعیین کرد.
Hwnd
Hwnd Handle پنجرهٔ Form یا Report را برمیگرداند و بهویژه در API Callهایی که Window Handle میخواهند کاربرد دارد. فصل ۲۰ API Callها را توضیح میدهد:
MsgBox Me.Hwnd
Picture
Me.Picture = "C:\Users\Richard\Pictures\MyPicture.jpg"
Background Pictureِ Form یا Report را تنظیم میکند. پس از Load شدن، Picture بخشی از Form/Report میشود و دیگر به Path فایل وابسته نیست.
Printer
MsgBox Me.Printer.DeviceName
به Printer اختصاصیافته به Form یا Report و Propertyهای آن دسترسی میدهد.
RecordSource
MsgBox Me.RecordSource
Record Source متصل به Form/Report مانند Table یا Query را نشان میدهد.
Refresh
Me.Refresh
Recordهای زیربنایی Form را فوراً Update میکند تا تغییرهای کاربر اعمال شوند.
Repaint
اگر Process طولانی باعث شود Form نصفهونیمه Draw شده باقی بماند، Repaint Screen Update و Recalculationهای معوق را کامل میکند:
Me.Repaint
Requery
Requery Source فرم را دوباره Query و Controlها را Update میکند. در محیط Multiuser ممکن است کاربر دیگری Table را تغییر داده باشد و Form جاری دادهٔ Stale نشان دهد:
Me.Requery
RibbonName
نام Ribbon ویژهٔ Form یا Report را نگه میدارد. اگر Empty باشد Ribbon پیشفرض نمایش داده میشود. فصل ۱۱ ساخت Custom Ribbon را توضیح داده است:
Me.RibbonName = "MyNewRibbon"
ScrollBars
| Value | Setting |
|---|
| 0 | بدون Scroll Bar |
| 1 | فقط Horizontal |
| 2 | فقط Vertical |
| 3 | هر دو |
Me.ScrollBars = 2
این کد Vertical Scroll Bar را روی Form فعال میکند.
SetFocus
Focus را روی Form یا Report قرار میدهد؛ پیش از آن Focus ممکن است روی Control یا Object دیگری بوده باشد. متن منبع Statement را به این شکل چاپ کرده است:
Me,SetFocus
Undo
با Undo میتوان به کاربر اجازه داد تغییرهایش را ذخیره نکند. مثلاً در Eventِ Form_BeforeUpdate میتوان Message Box تأیید نمایش داد و اگر کاربر No را انتخاب کرد همهٔ تغییرها را Undo کرد:
Me.Undo
Visible
Form یا Report را بدون بستن مخفی میکند. True یعنی Visible و False یعنی Hidden:
Me.Visible = False
CurrentDb Object
CurrentDb Database جاری را از نظر Tableها، Queryها و Recordsetها نمایندگی میکند و در VBA مسیر اصلی ارتباط با Table و Query است.
CreateQueryDef
CurrentDb.CreateQueryDef "NewQuery", "select * from MyTable"
Query جدیدی با نام NewQuery و SQL مشخصشده میسازد.
CreateTableDef
برای ساخت Table Definition جدید استفاده میشود، اما برخلاف CreateQueryDef باید Fieldها نیز جداگانه تعریف و TableDef نهایی به Database Append شود:
Sub CreateTable()
Dim MyTable As TableDef
Set MyTable = CurrentDb.CreateTableDef("NewTable")
MyTable.Fields.Append MyTable.CreateField("LastName", dbText, 100)
CurrentDb.TableDefs.Append MyTable
End Sub
ابتدا TableDef ساخته میشود، سپس Fieldِ LastName با Type متنی و Size برابر 100 Character ایجاد و به Fields Append میشود و در پایان NewTable به Collectionِ TableDefs در Database جاری Append میشود.
Execute
Execute برای اجرای SQL Statementهایی است که Record برنمیگردانند، مانند Delete، Update، Create Table یا Insert:
CurrentDb.Execute ("delete * from newTable")
هنگام Execute هیچ Warning Message نمایش داده نمیشود؛ بنابراین نیازی نیست Warningها را قبل و بعد از اجرا خاموش/روشن کنید.
Name
MsgBox CurrentDb.Name
Path و Filenameِ Database جاری را برمیگرداند. اگر فقط Path لازم است از CurrentProject.Path استفاده کنید.
OpenRecordset
OpenRecordset یک Recordset Object میسازد و مقدمهٔ Delete، Add و Edit Recordهاست:
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("MyTable")
Parameter نخست میتواند Table Name، Query Name یا SQL String برگردانندهٔ Record باشد. Parameterهای اختیاری مهم:
- Type: Constant از
RecordsetTypeEnum مانند Dynaset یا Snapshot؛ Dynaset Read/Write و Snapshot Read-only است.
- Options: Constant از
RecordsetOptionEnum برای ویژگیهایی مانند Append Only یا Read Only.
- LockEdit: Constant از
LockTypeEnum برای Locking مانند Optimistic یا Pessimistic.
QueryDefs
Collection تمام Query Definitionهای Database است. میتوان نام و SQL همهٔ Queryها را پیمایش کرد:
Dim Qd As QueryDef
For Each Qd In CurrentDb.QueryDefs
MsgBox Qd.Name & " " & Qd.SQL
Next Qd
همین Collection برای تغییر SQL یک Query نیز قابل استفاده است.
TableDefs
Collection تمام Table Definitionهاست:
Dim Td As TableDef
For Each Td In CurrentDb.TableDefs
MsgBox Td.Name & " " & Td.Fields.Count
Next Td
نام همهٔ Tableها و تعداد Fieldهای هرکدام را نمایش میدهد.
Recordset Object
Recordset Recordsetهای باز بر پایهٔ Table یا Query را نمایندگی میکند و برای Delete، Edit و Update Recordها به کار میرود. معمولاً همراه CurrentDb.OpenRecordset استفاده میشود:
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("MyTable")
Do Until RecSet.EOF
MsgBox RecSet![MyField]
RecSet.MoveNext
Loop
این مثال در Recordsetِ MyTable حرکت و MyField هر Record را نمایش میدهد. در مثالهای بعد Variableِ RecSet همین Object را نمایندگی میکند.
AddNew
RecSet.AddNew
RecSet![MyField1] = "MyData1"
RecSet![MyField2] = "MyData2"
RecSet.Update
Record خالی جدید میسازد و پس از مقداردهی Fieldها، Update آن را در Table مینویسد. داده باید Rules هر Field را رعایت کند؛ Text در Numeric Field یا خالیگذاشتن Required Field Error ایجاد میکند.
BOF
MsgBox RecSet.BOF
True/False برمیگرداند که آیا Recordset در Beginning Of File قرار دارد.
Close
RecSet.Close
Set RecSet = Nothing
بستن Recordset در انتهای کد مهم است، زیرا در غیر این صورت Table زیربنایی همچنان In Use دیده میشود و ممکن است نتوان Structure آن را تغییر داد. Set به Nothing نیز Memory را آزاد میکند؛ Recordsetهای بزرگ میتوانند Memory زیادی مصرف کنند.
Delete
RecSet.Delete
Record جاری را حذف میکند و برخلاف AddNew پس از Delete نیازی به Update نیست.
Edit
RecSet.Edit
RecSet![MyField1] = "MyNewData1"
RecSet![MyField2] = "MyNewData2"
RecSet.Update
Record را وارد Edit Mode میکند و پس از تغییر Fieldها، Update داده را در Table مینویسد. Rules مربوط به Data Type و Required Field همچنان باید رعایت شوند.
EOF
MsgBox RecSet.EOF
True/False نشان میدهد Recordset به End Of File رسیده است.
Fields
Collectionِ تمام Fieldهای Recordset است و برای بررسی جزئیات Field پیش از Update مفید است:
With RecSet.Fields(0)
MsgBox .Name
MsgBox .Type
MsgBox .Value
MsgBox .Size
End With
جزئیات نخستین Field را نمایش میدهد؛ Index از صفر شروع میشود.
FindFirst، FindLast، FindNext و FindPrevious
این Methodها با Criteria مشخص Record مناسب را پیدا میکنند. Recordset باید بهصورت Snapshot یا Dynaset باز شده باشد. نمونهٔ FindFirst:
Dim RecSet As Recordset
Set RecSet = CurrentDb.OpenRecordset("MyTable", dbOpenDynaset)
RecSet.FindFirst ("MyField='Data'")
MsgBox RecSet!MyField
RecSet.close
Set RecSet=Nothing
به نخستین Recordی میرود که MyField برابر Data است. برای آخرین تطبیق:
RecSet.FindLast ("MyField='Data'")
برای تطبیق بعدی:
RecSet.FindNext ("MyField='Data'")
اگر Recordset هنوز روی نخستین Record باشد، FindNext میتواند نتیجهای مشابه FindFirst داشته باشد. برای تطبیق قبلی:
RecSet.FindPrevious ("MyField='Data'")
اگر Recordset روی آخرین Record باشد، FindPrevious میتواند نتیجهای مشابه FindLast بدهد.
MoveFirst، MoveLast، MoveNext و MovePrevious
RecSet.MoveFirst
RecSet.MoveLast
RecSet.MoveNext
RecSet.MovePrevious
بهترتیب نخستین، آخرین، بعدی و قبلی را انتخاب میکنند. MoveNext هنگام Iterate کردن Recordset ضروری است و فراموشکردن آن یکی از خطاهای رایج است.
NoMatch
پس از Find مشخص میکند Criteria تطبیق پیدا کرده است یا نه:
RecSet.FindFirst ("MyField='Data'")
MsgBox RecSet.NoMatch
متن منبع آن را بهعنوان Property قابل استفاده در If برای تصمیمگیری بعدی معرفی میکند.
RecordCount
MsgBox RecSet.RecordCount
تعداد Recordهای Recordset را برمیگرداند و برای بررسی اینکه Query یا Table داده دارد یا نه مفید است.
Requery
Recordset را دوباره Query میکند. در محیط Multiuser ممکن است کاربران دیگر Table زیربنایی را تغییر داده باشند و دادهٔ Recordset جاری قدیمی شده باشد.
Update
Update پس از AddNew یا Edit تغییرها را در Table زیربنایی ثبت میکند. بدون Update، تغییرهای انجامشده از بین خواهند رفت.
صفحهٔ پایانی این فصل در نسخهٔ اصلی عمداً خالی است.