محمد حجازي قام بنشر أبريل 24, 2005 قام بنشر أبريل 24, 2005 السلام عليكم ... أعتذر أولاً عن هذا العنوان الغريب ، ولكن كان لا بد منه (لزوم التشويق :d ). عندما يخوض المرء في غمار الـ VBA فإنه يكتشف الكثير و الكثير من الثنائيات المميزة التي لا يجدها في كتب البرمجة التقليدية . هذه الثنائيات ترقى بالمبرمج إلى مستوى الاحتراف و تسهل عليه الكثير من الأمور. سوف أقوم إنشاء الله بعرض متتابع لهذه الثنائيات و ذلك على شكل مشاركات مرتبة و مرقمة في هذا الموضوع وذلك كلما خطرت على بالي واحدة منها ، وهذا الموضوع مفتوح للنقاش من كل الأعضاء ، ولكن رجائي لكل من يريد عرض تجاربه التقيد بالترقيم الموضح للمشاركات . وشكراً سلفاً على التفاعل 1
محمد حجازي قام بنشر أبريل 24, 2005 الكاتب قام بنشر أبريل 24, 2005 1. الفرق بين Select و Activate : قد يتساءل المرء : ما الفرق بين تحديد الهدف Select وتنشيطه Activate : قد يكون من الصعوبة بمكان الإجابة عن هذا السؤال ببضع كلمات ، و لكن وكما تعودنا ، فإن الأمثلة هي خير طريقة لإيصال المعلومات ، لاحظ الكود التالي : Sub SelectAndActivate1() With Sheets("Sheet1") .Range("A1:E10").Select .Range("C5").Activate End With End Sub نلاحظ أننا قمنا بتحديد المجال A1:E10 و لكن الخلية النشطة في هذا المجال (الخلية الجاهزة لإستقبال البيانات) هي الخلية C5 . يمكننا تطبيق نفس الأسلوب السابق على أوراق العمل ، لاحظ الكود التالي : Sub SelectAndActivate2() Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select Sheets("Sheet2").Activate End Sub ولكن يجب عليك الانتباه إلى أن هذه الميزة متوفرة فقط في حال كون الهدف المنشط ينتمي إلى مجال الأهداف المحددة ، لفهم الموضوع بصورة أوضح راقب ما ينج عن هذه الأكواد : Sub SelectAndActivate3() With Sheets("Sheet1") .Range("A1:E10").Select .Range("H6").Activate End With End Sub Sub SelectAndActivate4() Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select Sheets("Sheet4").Activate End Sub لاحظ أن الهدف المحدد هو نفسه الهدف المنشط :p خذ هذا الأمر في الحسبان :pp: نفس الشيء يحصل عندما يكون الهدف المحدد هو هدف واحد ، في هذه الحالة فإن الهدف المحدد يكون منشطاً بصورة تلقائية ، وهنا لا فرق نستطيع استناجه بين هذين الكودين : Sub SelectAndActivate5() Sheets("Sheet1").Activate End Sub Sub SelectAndActivate6() Sheets("Sheet1").Select End Sub مرفق ملف يتضمن الحالات السابقة مع أكوادها : بالتوفيق Select_Activate.zip 1
محمد حجازي قام بنشر أبريل 24, 2005 الكاتب قام بنشر أبريل 24, 2005 2. الفرق بين Sheets و Worksheets : الكلمة المحجوزة Worksheets تستخدم للإشارة إلى أوراق العمل فقط في المصنف ، أما الكلمة المحجوزة Sheets فتستخدم للإشارة إلى الأوراق بكافة أنواعها (أوراق العمل ، أوراق التخطيط ، ... ، الخ) في المصنف ، لاحظ الأكواد التالية : Sub WorksheetsAndSheets1() Sheets("Sheet1").Activate End Sub Sub WorksheetsAndSheets2() Sheets("Chart1").Activate End Sub Sub WorksheetsAndSheets3() Worksheets("Sheet1").Activate End Sub Sub WorksheetsAndSheets4() Worksheets("Chart1").Activate End Sub نلاحظ أن الكود الرابع يعطي الخطأ رقم 9 عند تشغيله :Subscript out of range و هذا ينطبق مع ما قلناه سابقاً ولكن ألم يخطر في بالك السؤال التالي: لماذا لا نستخدم الكلمة المحجوزة Sheets في جميع أكوادنا ؟!!!! هذا بالفعل ما أقوم به وذلك لأن الكلمة المحجوزة Sheets أسهل بالكتابة و أريح على الذهن :d ، ولكن مهلاً يمكننا الاستفادة من التفريق السابق في أكوادنا : بفرض أنه لدينا مصنف يحتوي على ثلاثة أوراق عمل Sheet1 ، Sheet2 ، Sheet3 و ورقة تخطيط واحدة Chart1 ، وبفرض أن ترتيب الأوراق كان كما يلي : Sheet1 ، Sheet2 ، Chart1 ، Sheet3 الآن لنقم بتطبيق الكود التالي : Sub WorksheetsAndSheets5() Worksheets(3).Activate End Sub نلاحظ أنه تم تنشيط الورقة Sheet3 ، هل تعرف لماذا؟ الكود السابق يقوم بتنشيط ورقة العمل الواقعة في الترتيب الثالث ، وبما أننا هنا نتعامل مع الكلمة المحجوزة Worksheets فإنه لم يتم أخذ الورقة Chart1 في الحسبان أثناء حساب ترتيب الأوراق. مرفق ملف يتضمن الحالات السابقة مع أكوادها : بالتوفيق Worksheets_Sheets.zip 1
محمد حجازي قام بنشر مايو 10, 2005 الكاتب قام بنشر مايو 10, 2005 3. الفرق بين Range و Cells : - يستخدم الأسلوب Range للإشارة إلى مجال الخلايا و ذلك عن طريق كتابة المجال باستخدام نظام الترميز A1 (تتم الإشارة إلى خلية ما بإدخال حرف العمود متبوعاً برقم الصف) ، لاحظ الأكواد التالية : Sub Test1() Sheets("Sheet1").Range("A1").ClearContents End Sub الكود السابق يقوم بمسح القيم الموجودة في الخلية A1 . Sub Test2() Sheets("Sheet1").Range("B2:C5").ClearContents End Sub الكود السابق يقوم بمسح القيم الموجودة في المجال B2:C5 . Sub Test3() Sheets("Sheet1").Range("A2:B3,D3,E5:G7,H11").ClearContents End Sub الكود السابق يقوم بمسح القيم الموجودة في المجالات A2:B3 ، D3 ، E5:G7 ، H11 . - أما الأسلوب Cells فيستخدم للإشارة إلى الخلايا المختلفة عن طريق كتابة رقم صف الخلية يليه رقم العامود ، لاحظ الكود التالي : Sub Test4() Sheets("Sheet1").Cells(2, 5).ClearContents End Sub الكود السابق يقوم بمسح القيم الموجودة في الخلية الواقعة في الصف الثاني و العامود الخامس من الورقة Sheet1 (أي الخلية E2). بالنسبة لقدرة الأسلوب Cells على الإشارة للمجالات المختلفة فهي محدودة ، ولا يستطيع ذلك إلى عن طريق الاستعانة بالأسلوب Range ، لاحظ الكود التالي : Sub Test5() Sheets("Sheet1").Range(Cells(2, 3), Cells(5, 7)).ClearContents End Sub الكود السابق يقوم بمسح القيم الموجودة في المجال C2:G5 الموجود في الورقة Sheet1 . مما سبق فإنه يبدو لنا أن التعامل مع الأسلوب Range هو أسهل بكثير من التعامل مع مثيلها Cells ، ولكن مهلاً ....... فالأسلوب Cells يمتلك مرونة عالية في التعامل مع الأهداف المتغيرة ، لاحظ الكود التالي : Sub MultiplicationTable1() Dim i As Byte, ii As Byte With Sheets("Sheet1") For i = 1 To 10 For ii = 1 To 10 .Cells(i, ii) = i * ii Next ii Next i End With End Sub الكود السابق هو كود مدرج في أحد مشاركاتنا السابقة ، ويقوم بإنشاء جدول الضرب في الورقة Sheet1 . لاحظ مقدار المرونة الذي يوفره لنا الأسلوب Cells ، و فكر بالصعوبات التي من المحتمل أن تواجهنا عند استخدام الأسلوب Range (و خصوصاً عند التعامل مع ترتيب الأعمدة) . مثال آخر يظهر مرونة الأسلوب Cells : بفرض أن لدينا جدول متوضع بين العامودين B و E ، ونريد إنشاء زر أمر لمسح قيم أي سجل بالاعتماد على رقمه . بالاعتماد على الأسلوب Cells فإننا نستطيع كتابة الكود التالي : Sub DeleteRecord1() Dim NumberRow As Long NumberRow = Application.InputBox(prompt:="ادخل رقم الصف", Title:="رقم الصف", Type:=1) If NumberRow = False Or NumberRow > 65536 Then Exit Sub Sheets("Sheet1").Range(Cells(NumberRow, 2), Cells(NumberRow, 5)).ClearContents End Sub الآن إذا أردنا الاعتماد على الأسلوب Range في الكود السابق عوضاً عن الأسلوب Cells ، فإننا سنحتاج لتعريف متغير إضافي MyRange من نوع نص و كتابة سطر إضافي لإنشاء المرجع المطلوب قبل استخدامه في الأسلوب Range ، لاحظ الكود التالي : Sub DeleteRecord2() Dim NumberRow As Long Dim MyRange As String NumberRow = Application.InputBox(prompt:="ادخل رقم الصف", Title:="رقم الصف", Type:=1) If NumberRow = False Or NumberRow > 65536 Then Exit Sub MyRange = "B" & NumberRow & ":E" & NumberRow Sheets("Sheet1").Range(MyRange).ClearContents End Sub بنظرة بسيطة نلاحظ أ الكود الأول هو أكثر كفاءة من الكود الثاني . لأخذ العلم ، فإن الطريقة السابقة أدرجت لتبيان الفرق بين الأسلوبين ، وذلك مع العلم بأنه يمكن حل المشكلة السابقة بطريقة أكثر بساطة و ذلك إذا ما اعتمدنا على الأسلوب Offset . ملاحظات هامة جداً عن الأسلوبين Cells و Range : 1. عندما تجري عملية مناقلة بين خلايا معينة ، فإنه لا يشترط استخدام نفس الأسلوب للإشارة إلى الخلايا أو النطاقات في كلتا المجموعتين : فمثلاً إذا أردنا لصق قيم المجال A1:A5 في المجال B1:B5 فإننا نستطيع عمل ذلك باستخدام أي طريقة من الطرق التالية : Sub Test6() With Sheets("Sheet1") Range("B1:B5").Value = Range("A1:A5").Value End With End Sub Sub Test7() With Sheets("Sheet1") Range(Cells(1, 2), Cells(5, 2)).Value = Range(Cells(1, 1), Cells(5, 1)).Value End With End Sub Sub Test8() With Sheets("Sheet1") Range(Cells(1, 2), Cells(5, 2)).Value = Range("A1:A5").Value End With End Sub Sub Test9() With Sheets("Sheet1") Range("B1:B5").Value = Range(Cells(1, 1), Cells(5, 1)).Value End With End Sub 2. قدرة كلا الأسلوبين Cells و Range على التكيف مع مجموعات الأهداف المختلفة : وهذه برأيي أهم ميزة يمتلكها هذين الأسلوبين ، وتقوم هذه الميزة على أن الأسلوب يشير للموضع النسبي للهدف المحتضن بالاعتماد على الهدف الحاضن له . لفهم هذه الميزة بشكل أوضح لاحظ الكودين التاليين : Sub Cells_Place1() Dim MyRange As Range Set MyRange = Sheets("Sheet1").Range("C5:H10") Sheets("Sheet1").Cells(1, 1).Value = "هذه الخلية الأولى من الورقة Sheet1" MyRange.Cells(1, 1).Value = "هذه الخلية الأولى من المجال MyRange" End Sub Sub Cells_Place2() Dim MyRange As Range Set MyRange = Sheets("Sheet1").Range("C5:H10") Sheets("Sheet1").Range("A1").Value = "هذه الخلية الأولى من الورقة Sheet1" MyRange.Range("A1").Value = "هذه الخلية الأولى من المجال MyRange" End Sub نلاحظ أن الأسلوبين Cells و Range قد اعتمدا على كل من الهدفين Sheet1 و MyRange ـ (C5:H10) من أجل تحديد الهدف الناتج (المحتضن) . بالنسبة لي ، فإنني أعتقد أن الأسلوب Cells أقدر على التعامل مع هذه الميزة بصورة أكثر كفاءة ، لاحظ الكود التالي : Sub Cell_Index() On Error GoTo NoRange Dim MyRange As Range Dim NumberRow As Long Dim NumberColumn As Long Set MyRange = Application.InputBox(prompt:="أدخل مجال الخلايا الذي تريده", Title:="مجال الخلايا", Type:=8) On Error GoTo 0 For NumberRow = 1 To MyRange.Rows.Count For NumberColumn = 1 To MyRange.Columns.Count MyRange.Cells(NumberRow, NumberColumn).Value = NumberRow & "×" & NumberColumn Next NumberColumn Next NumberRow Exit Sub NoRange: If Err = 424 Then Exit Sub Else MsgBox Err.Description End If End Sub الكود السابق يقوم بإدراج ترتيب كل خلية (رقم صفها و عامودها) وذلك بالنسبة للهدف الحاضن MyRange . لا تقلق أخي إذا لم تفهم هذه الميزة بصورة كاملة ، وذلك لأن لنا عودة معها عندما نقوم بشرح الأسلوبين CurrentRegion و UsedRange . تحياتي 1
الردود الموصى بها