اشیای اصلی Access | Microsoft Access 2010 VBA

اشیای اصلی Access

توسط admin | گروه آموزش اکسس Microsoft access | 1405/05/16

نظرات 0

اشیای اصلی Access

فصل ۱۵ — اشیای اصلی

در فصل قبل، مدل‌های شیء را از نظر نحوهٔ کار، 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های زیر نگه می‌دارد:

  • AllForms
  • AllReports
  • AllMacros
  • AllModules

این 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

ValueSetting
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، تغییرهای انجام‌شده از بین خواهند رفت.

صفحهٔ پایانی این فصل در نسخهٔ اصلی عمداً خالی است.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500