اذهب إلى المحتوى

حسام برهان

الأعضاء
  • المساهمات

    215
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    31

كل منشورات العضو حسام برهان

  1. نتابع عملنا في سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms. سنتعلّم في هذا الدرس تقنيّات جديدة لبناء واجهات متقدّمة أكثر باستخدام رماز XAML. كما سنتعلّم كيفيّة عرض رسائل تنبيه للمستخدم. يُعتبر هذا الدرس مساعدًا للدرس السابق في فهم كيفية التعامل مع رماز XAML. سنتناول في هذا الدرس تطبيقًا عمليًا مفيدًا، الهدف منه هو حل معادلة من الدرجة الثانيّة Quadratic Equation في مجموعة الأعداد الحقيقية R. كيفيّة حل معادلة من الدرجة الثانية للمعادلة من الدرجة الثانية الشكل العام التالي: لكي نحل هذه المعادلة في المجموعة R نحتاج إلى معرفة قيم المعاملات a وb وc وهي عبارة عن ثوابت حقيقيّة سنطلب من المستخدم أي يزوّد البرنامج بها. بعد ذلك سنطبّق القانون التالي لحساب مميّز هذه المعادلة: من خلال قيمة هذا المميّز نكون أمام ثلاث حالات: الحالة الأولى عندما يكون فيوجد عندئذ حلّين مختلفين للمعادلة المفروضة يمكن الحصول عليهما من العلاقتين التاليتين: الحالة الثانية عندما فيوجد عندئذ حل مضاعف يُعطى بالعلاقة التالية: الحالة الثالثة عندما فلا يكون للمعادلة عندها أي حل في المجموعة R. بناء تطبيق حل المعادلات من الدرجة الثانية ابدأ بإنشاء مشروع جديد من النوع Blank App (Xamarin.Forms Portable) وسمّه SDESolverApp، ثم أبق فقط على المشروعين SDESolverApp (Portable) و SDESolverApp.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك سنضيف صفحة محتوى تعتمد على رماز XAML كما وسبق أن فعلنا في الدرس السابق سنسمّها SDESolverPage. سنقسّم الواجهة إلى ثلاثة أقسام. القسم الأوّل العلوي سيحتوي على رسالة ترحيبيّة، أمّا القسم الأوسط فسيضم المنطقة الخاصّة بإدخال قيم المعاملات a وb وc الذين تحدثنا عنهم قبل قليل مع لصيقة توضيحيّة. أمّا القسم الأخير السفلي فسيحوي زرًا عند نقره سيتم حل المعادلة. انظر الشكل التالي لمعرفة الشكل العام لهذا التطبيق: في البداية أقترح وضع الواجهة السابقة ضمن مخطّط مكدّس يحيط بها بشكل كامل وذلك على الشكل التالي: <StackLayout> <StackLayout.Children> … </StackLayout.Children> </StackLayout> تمثّل النقاط الثلاث التي تظهر في الرماز السابق، المكان المفترض لرماز الواجهة الذي سنتحدّث عنه بعد قليل، حيث ستقع أي عناصر مرئيّة أخرى ضمن العنصر StackLayout.Children كما هو معلوم. لنجهّز الرسالة الترحيبيّة التي ستظهر في الأعلى. في الحقيقة تتألّف هذه الرسالة من عنصر إطار Frame يحوي لصيقة تعرض النص الترحيبي. يمكن استخدام الرماز التالي لتحقيق هذا الشكل: <Frame Padding="5" OutlineColor="Accent" BackgroundColor="#300000FF" VerticalOptions="StartAndExpand"> <Frame.Content> <Label Text="تطبيق حل المعادلات من الدرجة الثانية" FontSize="Large" TextColor="Accent" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" /> </Frame.Content> </Frame> بالنسبة لعنصر الإطار Frame فقد ضبطنا الخاصيّة Padding له لإضافة حشوة صغيرة تحيط بمحتوياته. كما ضبطنا خصائص لون الحد الخارجي OutlineColor لتحمل اللون Accent وخاصيّة لون الخلفية BackgroundColor لتكون "#300000FF". القيمة السابقة هي قيمة ستة عشرية تتكوّن من أربعة مكوّنات. كل مكّون يحجز محرفين. هذه المكوّنات من اليسار إلى اليمين: قيمة الشفافيّة alpha وتتراوح بين 00 و FF (استخدمنا في هذا المثال القيمة 30)، ثم يأتي اللون الأحمر فالأخضر فالأزرق. فمن خلال القيمة "#300000FF" سيظهر معنا لون أزرق لكنّه شفّاف بعض الشيء. ضبطنا أيضًا خاصيّة التموضع الرأسي VerticalOptions لتكون StartAndExpand ليظهر الإطار في البداية (في الأعلى في هذا التطبيق). بعد ذلك تأتي الخاصيّة Frame.Content والتي يمكن إسناد عنصر مرئي واحد إليها. أسندنا لصيقة كما هو واضح في الرماز السابق، وضبطنا خصائصها ليكون حجم الخط كبيرًا ولونه Accent، كما جعلنا محاذاة النص ضمن اللصيقة في الوسط. تنحصر مهمّة عنصر الإطار في إكساب ناحية جمالية للصيقة التي يحتويها. لننتقل الآن إلى القسم الثاني الأوسط من واجهة التطبيق والذي يحتوي كما أشرنا على منطقة مدخلات المستخدم. سنضع هذا القسم بكامله ضمن مخطّط مكدّس. سيحتوي هذا المخطّط على ما يلي: 1- لصيقة ضمن إطار كما فعلنا تمامًا مع الرسالة الترحيبيّة السابقة في القسم العلوي. تحوي هذه اللصيقة نصًّا توضيحيًّا. لننظر إلى الرماز المسؤول عن إظهار اللصيقة التي تحتوي على النص التوضيحي: <StackLayout VerticalOptions="CenterAndExpand"> <StackLayout.Children> <Frame Padding="5" OutlineColor="Green" BackgroundColor="#3000FF00"> <Frame.Content> <Label Text="أدخل المعاملات التالية لإيجاد حل المعادلة " FontSize="Medium" TextColor="Lime" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="End" /> </Frame.Content> </Frame> ... </StackLayout.Children> </StackLayout> النقاط الثلاث الموجودة ضمن الرماز السابق هي مكان الرماز الذي سيُظهِر مربّعات النص المسؤولة عن استقبال الدخل من المستخدم، حيث تجنبّت وضعه صراحةً لكي نركّز الآن على الرماز الحالي. لاحظ عنصر الإطار Frame كيف يحتوي ضمن الخاصيّة Content له على اللصيقة التي تعرض العبارة "أدخل المعاملات التالية لإيجاد حل المعادلة". لقد ضبطت الخاصيّة HorizontalTextAlignment لها لتكون End أي ليظهر النص كما لو أنّه محاذًا نحو اليمين. 2- مخطّط مكدّس آخر لعرض مربّعات الإدخال Entry لاستقبال الدخل من المستخدم، وهو يقع أسفل الإطار الذي يحوي اللصيقة التي تعرض العبارة التوضيحيّة (انظر رقم 1): <StackLayout VerticalOptions="CenterAndExpand"> <StackLayout.Children> ... <StackLayout Orientation="Horizontal"> <StackLayout.Children> <Entry x:Name="txtA" HorizontalOptions="StartAndExpand" Placeholder="المعامل a" FontSize="Medium" Keyboard="Telephone" /> <Entry x:Name="txtB" HorizontalOptions="CenterAndExpand" Placeholder="المعامل b" FontSize="Medium" Keyboard="Telephone" /> <Entry x:Name="txtC" HorizontalOptions="EndAndExpand" Placeholder="المعامل c" FontSize="Medium" Keyboard="Telephone" /> </StackLayout.Children> </StackLayout> </StackLayout.Children> </StackLayout> الأمر الواضح هنا أنّني قد وضعت مربّعات النص Entry لاستقبال الدخل من المستخدم ضمن مخطّط مكدّس آخر، وهذا أمر طبيعي تمامًا في تصميم الواجهات. لقد جعلت خاصيّة الاتجاه Orientation لهذا المكدّس تحمل القيمة Horizontal أي أفقي. كما وضعت ضمن العنصر StackLayout.Children ثلاثة عناصر Entry لاستقبال المعاملات a وb وc على الترتيب. من الملاحظ أنّني استخدمت الخاصيّة x:Name لكلّ من هذه العناصر لكي نستطيع الوصول إلى محتوياتها ضمن ملف الشيفرة البرمجيّة المرافق لملف الرماز كما سنرى بعد قليل. كما استخدمت الخاصيّة Placeholder لكلّ منها لعرض نص توضيحي ضمن عنصر Entry يوضّح وظيفته. الأمر الأخير الملاحظ هنا هو استخدامي للخاصيّة Keyboard للعنصر Entry التي تسمح بتحديد لوحة المفاتيح التي ستظهر للمستخدم عندما يحاول الكتابة ضمن هذا العنصر. توجد عدّة لوحات مفاتيح تدعمها Xamarin.Froms. لقد استخدمت من أجل هذا المثال لوحة المفاتيح Telephone وهي لوحة مفاتيح مخصّصة لإدخال البيانات المتعلّقة بأرقام الهواتف. في الحقيقة توجد لوحة مفاتيح مخصّصة للأرقام اسمها Numeric ولكنّني آثرت لوحة مفاتيح Telephone عليها لأنّها تدعم إدخال إشارة السالب (-) وهذا ما لا توفّره لوحة المفاتيح الرقميّة القياسيّة Numeric. ننتقل الآن إلى القسم الأخير السفلي من الواجهة، وهو يحتوي على زر بسيط عندما ينقره المستخدم سيعمل البرنامج على حل المعادلة ويعرض النتيجة. انظر إلى الرماز الخاص به: <Button Text="حل المعادلة"` VerticalOptions="EndAndExpand" HorizontalOptions="FillAndExpand" HeightRequest="64" Clicked="btnSolve_Clicked"/> استخدمت هذه المرّة الخاصيّة HeightRequest التي يمكن من خلالها ضبط ارتفاع العنصر بشكل تقريبي. لقد جعلت ارتفاعه مساويًا لـ 1 سم تقريبًا. توجد خاصيّة أخرى مشابهة لها لضبط عرض أي عنصر بشكل تقريبي وهي WidthRequest. ضبطّت أيضًا معالج الحدث Clicked الذي سيتم تنفيذه عندما ينقر المستخدم على هذا الزر. اسم هذا المعالج btnSolve_Clicked وسيكون في الطبع ضمن ملف الشيفرة البرمجيّة. إليك الآن الرماز الكامل لهذا لواجهة هذا التطبيق مجمّعًا على الشكل التالي: <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SDESolverApp.SDESolverPage" Padding="5"> <StackLayout> <StackLayout.Children> <Frame Padding="5" OutlineColor="Accent" BackgroundColor="#300000FF" VerticalOptions="StartAndExpand"> <Frame.Content> <Label Text="تطبيق حل المعادلات من الدرجة الثانية" FontSize="Large" TextColor="Accent" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" /> </Frame.Content> </Frame> <StackLayout VerticalOptions="CenterAndExpand"> <StackLayout.Children> <Frame Padding="5" OutlineColor="Green" BackgroundColor="#3000FF00"> <Frame.Content> <Label Text="أدخل المعاملات التالية لإيجاد حل المعادلة " FontSize="Medium" TextColor="Lime" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="End" /> </Frame.Content> </Frame> <StackLayout Orientation="Horizontal"> <StackLayout.Children> <Entry x:Name="txtA" HorizontalOptions="StartAndExpand" Placeholder="المعامل a" FontSize="Medium" Keyboard="Telephone" /> <Entry x:Name="txtB" HorizontalOptions="CenterAndExpand" Placeholder="المعامل b" FontSize="Medium" Keyboard="Telephone" /> <Entry x:Name="txtC" HorizontalOptions="EndAndExpand" Placeholder="المعامل c" FontSize="Medium" Keyboard="Telephone" /> </StackLayout.Children> </StackLayout> </StackLayout.Children> </StackLayout> <Button Text="حل المعادلة" VerticalOptions="EndAndExpand" HorizontalOptions="FillAndExpand" HeightRequest="64" Clicked="btnSolve_Clicked"/> </StackLayout.Children> </StackLayout> </ContentPage> لننتقل إلى ملف الشيفرة البرمجيّة المرافق لملف الرماز السابق. اسم هذا الملف بالطبع هو SDESolverPage.xaml.cs. احرص على أن تكون محتوياته على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace SDESolverApp 5 { 6 public partial class SDESolverPage : ContentPage 7 { 8 public SDESolverPage() 9 { 10 InitializeComponent(); 11 } 12 13 private void btnSolve_Clicked(object sender, EventArgs e) 14 { 15 double a, b, c; 16 17 string txt_a = txtA.Text; 18 string txt_b = txtB.Text; 19 string txt_c = txtC.Text; 20 21 if (double.TryParse(txt_a, out a) && 22 double.TryParse(txt_b, out b) && 23 double.TryParse(txt_c, out c)) 24 { 25 string result; 26 27 double delta = b * b - 4 * a * c; 28 29 if (delta > 0) 30 { 31 //there are two different solutions. 32 double x1 = (-b + Math.Sqrt(delta)) / (2 * a); 33 double x2 = (-b - Math.Sqrt(delta)) / (2 * a); 34 35 result = string.Format("يوجد حلين مختلفين في المجموعة R\nx1 = {0}\nx2 = {1}", 36 x1.ToString("#0.#"), 37 x2.ToString("#0.#")); 38 } 39 else if (delta == 0) 40 { 41 //there are two equal solutions. 42 double x = -b / (2 * a); 43 44 result = string.Format("يوجد حل مضاعف في المجموعة R\nx1 = x2 = {0}", 45 x.ToString("#0.#")); 46 } 47 else 48 { 49 //there is no solutions in R. 50 result = "المعادلة مستحيلة الحل في المجموعة R"; 51 } 52 53 DisplayAlert("الحل", result, "موافق"); 54 } 55 else 56 { 57 DisplayAlert("خطأ في المدخلات", "بعض المعاملات المدخلة غير صحيحة", "موافق"); 58 } 59 } 60 } 61 } يحتوي الصنف SDESolverPage بشكل أساسيّ على معالج الحدث btnSolve_Clicked الذي سيُستدعى عندما ينقر المستخدم زر حل المعادلة من الواجهة. يحتوي هذا المعالج على كامل منطق التطبيق. يحصل البرنامج على قيم المعاملات في الأسطر من 17 حتى 19 حيث تكون قيم نصيّة بالطبع، ثمّ يحوّل هذه القيم النصيّة إلى قيم من النوع double وذلك في الأسطر من 21 حتى 23 من خلال استخدام التابع الساكن TryParse من الصنف double. يحاول هذا التابع أن يحوّل القيمة النصيّة المُمرّرة إليه في الوسيط الأوّل، فإن نجح في ذلك فإنّه يُسند القيمة المُحوّلة إلى المتغيّر a الذي نمرّره كوسيط ثانٍ إلى هذا التابع ويُرجع التابع القيمة true. أمّا إذا فشلت عمليّة التحويل فإنّ التابع سيُرجع القيمة false فيختلّ شرط عبارة if، وبالتالي لن يُتابع البرنامج عمله في حل المعادلة ويعرض رسالة بهذا الخصوص إلى المستخدم بسبب عدم صلاحيّة أحد العوامل المُدخلة على الأقل. لاحظ أنّنا قد مرّرنا الوسيط الثاني الذي سيحمل نتيجة التحويل في حال نجاحه باستخدام الكلمة المحجوزة out. التي تسمح بتمرير عنوان المتغيّر إلى التابع وليس قيمته. يتابع البرنامج عمله في حال نجحت عمليّة تحويل المعاملات إلى double بحساب مميّز المعادلة ومقارنته بالحالات الثلاث المختلفة التي تحدثنا عنها مسبقًا. توجد ملاحظة أخيرة في أنّ التطبيق يعرض النتائج باستخدام صناديق رسائل قياسيّة في أندرويد، وذلك من خلال استخدام التابع DisplayAlert وهو من الصنف Page (السطران 53 و57) أيّ أنّه ينتمي إلى الصنف SDESolverPage من خلال الوراثة. يخضع التابع DisplayAlert لزيادة التحميل، وقد استخدمت الشكل الذي يتطلّب ثلاثة وسائط وهي بالترتيب عنوان الرسالة، والرسالة المراد عرضها، ثم النص المراد عرضه على الزر. يمكن بالطبع استخدام وسائل مختلفة لعرض النتائج للمستخدم، كعرضها ضمن صفحة منفصلة مثلًا، وقد يكون هذا الأسلوب أفضل. ولكن بالنسبة إلى هذه المرحلة فالحل المستخدم في هذا التطبيق يُعتبر جيّدًا. سنتعلّم مستقبلًا كيفيّة التعامل مع عرض المعلومات المختلفة عن طريق صفحات مستقلّة. الخلاصة بنينا في هذا الدرس تطبيقًا عمليًّا، يوضّح التعامل مع رماز XAML بشكل متقدّم أكثر. في الحقيقة ستحتاج إلى تطوير العديد من البرامج البسيطة كهذا البرنامج كي تتمكّن من تصميم الواجهات بشكل جيّد. سنتابع في الدروس القادمة العمل في تطوير تطبيقات عمليّة بسيطة ومفيدة، يمكن من خلالها تعلّم المزيد عن بناء الواجهات باستخدام XAML.
  2. يُعتبر هذا الدرس من الدروس المهمّة في سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms. في الواقع لقد أشرنا إلى هذا الدرس عدّة مرّات خلال الدروس السابقة. تكمن المشكلة في التطبيقات التي تناولناها في الدروس السابقة إلى أنّنا كنّا ننشئ واجهة التطبيق باستخدام الشيفرة البرمجيّة بشكل كامل مما يؤدّي إلى مشكلة كبيرة في تنظيم البرامج لأنّنا في هذه الحالة سنضطر غالبًا إلى وضع الشيفرة المسؤولة عن بناء الواجهات مع الشيفرة البرمجيّة المسؤولة عن تمثيل منطق العمل business logic في البرنامج، وهذا أمر سيء بالطبع. وفّرت Xamarin حلًا ممتازًا لهذه المشكلة يجعل من البرمجة باستخدامها تجربة غنيّة ومعاصرة، وذلك من خلال إنشاء الواجهات باستخدام رماز XAML. يشبه هذا الرماز إلى حدٍّ كبير رماز XML. حيث يتمّ إنشاء العناصر المرئيّة وضبط خصائصها باستخدام ما يشبه عناصر وسمات XML. تكمن الفائدة الأساسيّة من استخدام هذا الأسلوب هو عزل بناء واجهات البرنامج عن الشيفرة البرمجيّة المسؤولة عن معالجة منطق البرنامج، وهذا أمر مهم وخصوصًا عند بناء تطبيقات كبيرة. لنقارن مثلًا بين إنشاء لصيقة باستخدام الشيفرة البرمجيّة وباستخدام XAML: لا أعتقد أنّنا سنبذل كثيرًا من الجهد لملاحظة أنّ أسلوب XAML مباشر وواضح أكثر من أسلوب الشيفرة البرمجيّة. تُكتَب العناصر المرئيّة في Xamarin.Forms باستخدام XAML على شكل عناصر XML عاديّة، أمّا خصائصها فتكون على شكل سمات attributes لهذه العناصر. يوجد أسلوب آخر لكتابة خصائص العناصر ضمن XAML سنتحدّث عنه بعد قليل. يُكتب رماز XAML ضمن ملفات مخصّصة لهذه الغرض تحمل الامتداد xaml. وطريقة إضافتها للمشروع بسيطة سنتحدّث عنها أيضًا بعد قليل ضمن برنامج تطبيقي. في الحقيقة يعمل المُعرب parser الخاص بـ XAML على ترجمة كل عنصر يصادفه إلى الصنف المقابل له في الشيفرة. فمثلًا ومن المثال البسيط السابق عند يصادف هذا المُعرب العنصر Label سيعمل على إنشاء كائن برمجي من الصنف Label ثم يعمل على مطابقة السمات الموجودة ضمن العنصر في XAML مع الخصائص الفعليّة للصنف Label ويُسند قيم الخصائص وفقًا لها. فمثلًا، تحمل السمة HorizontalTextAlignment القيمة Center من رماز XAML. وهي تُطابق الخاصيّة ذات الاسم نفسه من الصنف Label. لذلك فعند إنشاء كائن اللصيقة Label سيتم إسناد القيمة TextAlignment.Center إلى الخاصيّة HorizontalTextAlignment. مثال أخر حول السمة FontAttributes التي تحمل القيمة "Bold, Italic" وهي تُطابق الخاصيّة ذات الاسم نفسه من الصنف Label. لذلك فعند إنشاء كائن اللصيقة Label سيتم إسناد القيمة التالية لهذه الخاصيّة: FontAttributes.Bold | FontAttributes.Italic لاحظ العامل "|" وهو عامل OR على مستوى البتّات bits وهو يفيد في تعيين أكثر من قيمة بنفس الوقت للخاصيّة FontAttributes. ويسري هذا الأمر على باقي السمات للعنصر Label. مثال تطبيقي حول استخدام XAML في بناء الواجهات أنشئ مشروعًا جديدًا من النوع Blank App (Xamarin.Forms Portable) وسمّه XamlDemoApp، ثم أبق فقط على المشروعين XamlDemoApp (Portable) و XamlDemoApp.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك سنضيف صفحة محتوى تعتمد على رماز XAML. لفعل ذلك انقر بزر الفأرة الأيمن على المشروع XamlDemoApp (Portable) واختر Add ثم New Item. من مربّع الحوار الذي سيظهر لك، اختر من الجانب الأيسر له Cross-Platform، ومن القسم الأيمن احرص على اختيار Forms Xaml Page، وسمّها XamlDemoPage كما في الشكل التالي: انقر بعد ذلك الزر Add لإضافة هذه الصفحة إلى المشروع. ستحصل على الرماز التالي بشكل افتراضي ضمن هذه الصفحة: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="XamlDemoApp.XamlDemoPage"> 5 <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" /> 6 </ContentPage> يحتوي السطر الأوّل على إصدار XML المُستَخدم، أمّا السطر الثاني فيحتوي على عنصر ContentPage مما يشير إلى أنّنا فعليًّا نضيف صفحة محتوى content page ليس إلّا. يحتوي العنصر ContentPage على سمتين xmlns وxmlns:x وهما سمتان تعريفيّتان، أمّا السمة x:Class فهي تُشير إلى الصنف المرتبط مع هذه الصفحة وهو كما يظهر XamlDemoApp.XamlDemoPage حيث XamlDemoApp هو نطاق الاسم الخاص بالتطبيق. سنرى أين يوجد هذا الصنف بعد قليل. يحتوي السطر 5 على عنصر Label تجريبي يتم توليده بشكل افتراضي. أمّا في السطر السادس فيوجد عنصر الإغلاق لعنصر الفتح الموافق الموجود في السطر 2 وذلك كما هو متبع في XML. في الواقع عندما يضيف Visual Studio الملف XamlDemoPage.xaml فإنّه يضيف بشكل تلقائي الملف XamlDemoPage.xaml.cs أيضًا. واضح أنّ امتداد هذا الملف cs أي أنّه يحتوي على شيفرة سي شارب. انظر إلى الشكل التالي الذي يمثّل مستكشف الحل Solution Explorer بعد إضافة ملف المحتوى: الملف XamlDemoPage.xaml.cs هو ملف الشيفرة البرمجيّة المرافق لملف الواجهة XamlDemoPage.xaml ويحتوي على الصنف XamlDemoApp.XamlDemoPage الذي تحدثنا عنه قبل قليل. يحتوي هذا الصنف على معالجات أحداث event handlers تستجيب للأحداث المختلفة التي قد تحدث ضمن ملف الواجهة والناتجة عن مختلف أنواع العناصر المرئيّة الموجودة ضمنها. لنعد الآن إلى الملف XamlDemoPage.xaml، احذف العنصر Label الافتراضي الموجود في هذا الملف. سنضيف عناصر مرئيّة جديدة. احرص على أن تكون محتويات الملف XamlDemoPage.xaml على الشكل التالي: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="XamlDemoApp.XamlDemoPage"> 5 6 <StackLayout VerticalOptions="CenterAndExpand"> 7 <StackLayout.Children> 8 <Label VerticalOptions="Start" 9 HorizontalOptions="Center" 10 FontSize="Large" 11 TextColor="Accent" 12 x:Name="lblCurrentTime" 13 Text="Click the button!" /> 14 15 <Button VerticalOptions="End" 16 HorizontalOptions="Center" 17 FontSize="Medium" 18 x:Name="btnGetTime" 19 Text="Get Current Time" 20 Clicked="btnGetCurrentTime_Clicked"/> 21 </StackLayout.Children> 22 </StackLayout> 23 24 </ContentPage> يحتوي رماز XAML السابق على العديد من المفاهيم الجديدة ولكن البسيطة والتي سنلخّصها من خلال النقاط التالية: يضم الرماز واجهة بسيطة تتكوّن من لصيقة وزر عند نقره سيعرض الوقت الحالي على اللصيقة الموجودة فوقه. كلّ من هذين العنصرين سيكونان ضمن مخطّط مكدّس StackLayout. يبدأ هذا المخطّط في السطر 6 ويحوي سمة (خاصيّة) وحيدة هي VerticalOptions وتحمل القيمة CenterAndExpand. يمثّل عنصر XAML الموجود في السطر 7 وهو الخاصيّة Children لعنصر المكدّس StackLayout. نضع ضمن هذه الخاصيّة كما اعتدنا من قبل العناصر المرئيّة التي نرغب بوجودها ضمن المكدّس، وهذا ما سنفعله تمامًا بوضع عنصر اللصيقة (الأسطر من 8 حتى 13) وعنصر الزر (الأسطر من 15 حتى 20) ضمنه. وكما هو الحال بالنسبة لأي عنصر XML يجب إغلاق العنصر بوسم إغلاق موافق له، وهذا ما فعلناه في السطر 21. بالنسبة لعنصر اللصيقة، تمثّل السمات الموجودة ضمنها خصائص الصنف Label التي كنّا نتعامل معها في الشيفرة البرمجيّة في السابق. يوجد أمر بسيط هنا، وهو أنّ عنصر اللصيقة لا يوجد له وسم إغلاق مثل . في الحقيقة لا داعي له أبدًا، لأنّ اللصيقة لا يمكنها أن تحوي عناصر أخرى كما هو الحال بالنسبة لمخطّط المكدّس مثلًا. لقد أغلقنا اللصيقة بالرمزين /> فحسب، كما فعلنا مع الزر في السطر 20 تمامًا. توجد سمة اسمها x:Name ضمن عنصر اللصيقة (السطر 12) وأيضًا ضمن عنصر الزر (السطر 18). يمكن من خلال هذه السمة الوصول للعناصر المرئيّة وذلك من خلال الشيفرة البرمجيّة وهذا ما سنراه بعد قليل. في الواقع كان من الممكن ألًّا نستخدم هذه السمة مع عنصر الزر لأنّنا لا نحتاج إلى أن نصل إلى خصائصه بالنسبة لهذا البرنامج. معالج حدث النقر Clicked للزر في السطر 20. أسميته btnGetCurrentTime_Clicked، وهو غير موجود في هذا الملف بالطبع. يحتوي هذا الملف على شيفرة برمجيّة بلغة سي شارب تنفّذ المهمّة المطلوبة عند نقر هذا الزر. سيكون معالج الحدث btnGetCurrentTime_Clicked موجودًا في الملف XamlDemoPage.xaml.cs ضمن الصنف XamlDemoPage. انتقل إلى هذا الملف واحرص أن تكون محتوياته على الشكل التالي: using System; using System.Globalization; using Xamarin.Forms; namespace XamlDemoApp { public partial class XamlDemoPage : ContentPage { public XamlDemoPage() { InitializeComponent(); } private void btnGetCurrentTime_Clicked(object sender, EventArgs e) { lblCurrentTime.Text = DateTime.Now.ToString("h:mm:ss tt", CultureInfo.InvariantCulture); } } } توجد الشيفرة المسؤولة عن إظهار الوقت الحالي على اللصيقة ضمن معالج الحدث btnGetCurrentTime_Clicked كما هو واضح (يمكنك فهم هذه الشيفرة من خلال الانتقال إلى هذا الدرس ومراجعة تطبيق الساعة الرقميّة). انتقل إلى الملف App.cs وتأكّد أنّ بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new XamlDemoPage(); } نفّذ البرنامج باستخدام F5 ثم انقر الزر Get Current Time لتحصل على الوقت الحالي ضمن اللصيقة. انظر إلى الشكل التالي: هناك أمران ملفتان في الشيفرة السابقة: أسندنا قيمة الوقت الحالي إلى الخاصيّة Text من المتغيّر lblCurrentTime الذي يحمل نفس اسم اللصيقة التي صرّحنا عنها في ملف الواجهة. ولكن من أين أتي هذا المتغيّر إذا لم نصرّح عنه مطلقًا في ملف الشيفرة الحالي؟ لاحظ وجود استدعاء للتابع InitializeComponent ضمن بانية الصنف XamlDemoPage. هذا التابع ليس موجودًا ضمن الصنف ContentPage الذي يرث منه صنفنا XamlDemoPage، وهذا يعني أنّه يجب أن يكون ضمن الصنف XamlDemoPage ولكن هذا ما لا نراه أمامنا! الحيلة المستخدمة هنا هو في وجود الكلمة المحجوزة partial ضمن التصريح عن الصنف XamlDemoPage. تشير الكلمة partial إلى وجود جزء آخر لهذا الصنف ولكن في مكان ما! في الواقع هذا الجزء موجود بالفعل ولكن ضمن الملف XamlDemoApp.XamlDemoPage.xaml.g.cs. بما أنَّ هذا الملف له الامتداد cs هذا يعني أنّه يحتوي على شيفرة مكتوبة بلغة سي شارب، كما أنّ اسمه (الطويل نسبيًّا) مكوّن من نطاق الاسم واسم الصنف XamlDemoApp.XamlDemoPage ثم المقطع xaml للإشارة إلى أنّه متعلّق بملف الواجهة الذي يحتوي على رماز XAML والمقطع g الذي يأتي من الكلمة generated للإشارة إلى أنّ هذا الملف يتمّ توليده تلقائيًّا. إذا أردت مشاهدة الملف فاذهب إلى مستكشف الحل Solution Explorer ثم انقل على زر إظهار جميع الملفات Show All Files لتظهر المجلّدات والملفات الموجودة ضمن المشروع. انتقل إلى المجلّد obj ثم انشره لترى محتوياته. ستلاحظ وجود المجلّد Debug فقط ضمنه. انشره أيضًا لتصل إلى الملفات التي ضمنه حيث ستجد عندها الملف XamlDemoApp.XamlDemoPage.xaml.g.cs. انظر إلى الشكل التالي: انقره نقرًا مزدوجًا لترى محتوياته. ستجد الصنف XamlDemoPage مرّة أخرى، وهذا هو الجزء الذي نبحث عنه: 1 public partial class XamlDemoPage : global::Xamarin.Forms.ContentPage { 2 3 [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")] 4 private global::Xamarin.Forms.Label lblCurrentTime; 5 6 [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")] 7 private global::Xamarin.Forms.Button btnGetTime; 8 9 [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")] 10 private void InitializeComponent() { 11 this.LoadFromXaml(typeof(XamlDemoPage)); 12 lblCurrentTime = this.FindByName<global::Xamarin.Forms.Label>("lblCurrentTime"); 13 btnGetTime = this.FindByName<global::Xamarin.Forms.Button>("btnGetTime"); 14 } 15 } سنجد في السطر 4 التصريح عن المتغيّر lblCurrentTime الذي سيعمل التابع InitializeComponent (الأسطر من 10 حتى 15) على ربطه مع اللصيقة المصرّح عنها ضمن ملف الواجهة. كما أشرنا فإنّ هذا الملف يتم توليده تلقائيًّا وذلك بمجرّد إجراء أي عمليّة حفظ على الملف XamlDemoPage.xaml الذي يحتوي على واجهة التطبيق. الخلاصة تعلّمنا في هذه الدرس المبادئ الأساسيّة لبناء الواجهات باستخدام رماز XAML بدلًا عن أسلوب الشيفرة التي اعتمدناه في الدروس السابقة. ستلمس الفائدة العظيمة لبناء الواجهات بهذا الأسلوب عندما تبدأ ببناء تطبيقات واقعيّة، حيث سيجعل هذا الأسلوب من عمليّة بناء التطبيق أمرًا يسيرًا وواضحًا من خلال فصل الواجهة بما تحتويه من عناصر مرئيّة عن الشيفرة البرمجيّة المخصّصة للتفاعل مع هذه الواجهة، وذلك ضمن ملفّين منفصلين. تساعد هذه العمليّة على ترتيب البرامج وجعلها أسهل للفهم وللصيانة وللتطوير المستقبلي، وخصوصًا عندما تصبح البرامج أكبر ومعقّدة أكثر. هنالك الكثير مما يمكن قوله حول هذه التقنيّة سنتناول ذلك تباعًا من خلال الدروس القادمة. سنبني في الدرس القادم تطبيق عملي رياضيّاتيّ مفيد يتمثّل في حل معادلة من الدرجة الثانيّة حيث سنبني واجهة التطبيق من خلال XAML.
  3. مبدأ عكس التابعيّة Dependency Inversion Principle أو اختصارًا DIP، هو آخر مبادئ التصميم الكائنيّ SOLID ويتمتّع بمزايا كبيرة عند تطبيقه بالشكل السليم. أوّل من قدّم هذا المبدأ هو روبرت مارتن في مقالته التي نشرها عام 1996. أشار روبرت إلى أنّ الأسلوب الشائع في تصميم التابعيّة dependency ضمن المشاريع البرمجيّة في جعل الوحدات البرمجيّة عالية المستوى تعتمد على الوحدات البرمجيّة منخفضة المستوى بشكل مباشر هو أسلوب غير عمليّ ويؤدّي إلى مشاكل كبيرة عند إعادة استخدام الوحدات البرمجيّة عالية المستوى، تتمثّل هذه المشاكل في إجراء تعديلات برمجيّة عديدة عليها كي تتلاءم مع الاستخدام الجديد لها، وهذا بالطبع أمر غير جيّد. تُعتبر الوحدات البرمجيّة عالية المستوى بمثابة قلب التطبيق البرمجيّ. وقد نرغب في كثير من الأحيان أن نُعيد استخدامها في تطبيقات برمجيّة أخرى ولكن بدون إجراء تعديلات كبيرة عليها. يقترح روبرت مبدأ عكس التابعيّة والذي ينص على ما يلي: أ – لا ينبغي أن تعتمد الوحدات البرمجيّة عالية المستوى على الوحدات البرمجيّة منخفضة المستوى. يجب على كلّ منهما الاعتماد على واجهات (أصناف مجرّدة). ب – لا ينبغي أن تعتمد الواجهات/الأصناف المُجردّة على التفاصيل، فالتفاصيل هي من يجب أن تعتمد على الأصناف الواجهات. قد يكون من الصعب قليلًا فهم هذا المبدأ مباشرةً، لذلك اسمح لي بتوضيحه بشكل أفضل. يُقرّ هذا المبدأ أنّه ينبغي أن تكون هناك طبقة تجريديّة إضافيّة بين الوحدات البرمجيّة عالية ومنخفضة المستوى، تتألّف الطبقة التجريديّة من واجهات. فإذا كان لدينا وحدتان برمجيّتان أردنا أن تعتمد إحداهما على الأخر، فإنّ هذه الاعتماديّة ينبغي أن تتمّ عن طريق صنف واجهة معرّف خصيصًا لهذا الغرض. بهذا الأسلوب، فإنّ الوحدات البرمجيّة عالية المستوى لا تتعامل مباشرةً مع الوحدات البرمجيّة منخفضة المستوى. ستعمل الوحدات البرمجيّة منخفضة المستوى على تحقيق الواجهات (نستطيع القول أنّها سترث منها)، ففي حال أردنا استخدام أي وحدة برمجيّة في مشروع برمجيّ آخر فلن نحتاج إلى تعديل أيّ شيء ضمن الشيفرة البرمجيّة لها. فكل ما نحتاجه ببساطة هو تحقيق الواجهات التي ستعتمد عليها الوحدة البرمجيّة المراد إعادة استخدامها. بالنسبة للقسم الثاني من المبدأ (الفقرة ب) فهو ينبّه بأنّ الواجهات ينبغي ألّا تصمّم وفقًا للوحدات البرمجيّة منخفضة المستوى (التفاصيل). حيث ينبغي أن تُحقّق الأصناف الوجهات على نفس مستوى التجريد الذي تتمتّع به الوحدات البرمجيّة عالية المستوى. مثال عن عكس التابعيّة لنستعرض الآن مثالًا حول كيفية تطبيق هذا المبدأ. يدور هذا المثال حول التعامل مع الواجهات الرسوميّة للمستخدم، ليكن لدينا صنف يمثّل نافذة Window تحوي زرّين Button: class Button { public: void makeVisible(); }; class Window { Button* okButton; Button* cancelButton; Window() { okButton = new Button; okButton->makeVisible(); cancelButton = new Button; cancelButton->makeVisible(); } }; تكمن المشكلة هنا أنّه إذا تغيّر الصنف Button، سنضطّر إلى تغيير بانية الصنف Window أيضًا، وهذا بالطبع أمر غير مرغوب به، لأنّ الصنف Window سيكون عُرضةً للكثير من الاختبارات أثناء بناء البرنامج، وهذا سيؤدّي إلى الكثير من الأخطاء في كلّ مرّة نُعدّل فيها الصنف Button. يؤدّي استخدام صنف واجهة إلى تحسين التصميم إلى حدّ كبير: class IButton { public: static virtual IButton* getInstance() = 0; // factory method virtual void show() = 0; }; class Window { IButton* okButton; IButton* cancelButton; public: Window() { okButton = IButton::getInstance(); okButton->show(); cancelButton = IButton::getInstance(); cancelButton->show(); } }; class Button : public IButton { public: void show(); }; كما نرى الآن، يوجد صنف واجهة اسمه IButton وكل من الصنفين Button وWindow يعتمدان عليه. وهذا أمر جيّد للغاية، ففي حال أردنا استخدام الصنف Window في تطبيق برمجيّ جديد، فكل ما علينا هو استخدام أزرار Button تُحقّق الواجهة IButton فحسب. نلاحظ أيضًا التّابع الساكن getInstance الذي نستخدمه للحصول على الكائن الملائم من صنف الزر الذي يُحقّق صنف الواجهة IButton. المصادر http://www.objectmentor.com/publications/dip.pdf http://www.oodesign.com/dependency-inversion-principle.html http://en.wikipedia.org/wiki/Dependency_inversion_principle ترجمة -وبتصرّف- للمقال Dependency Inversion Principle لصاحبه Radek Pazdera.
  4. سنختتم في هذا الدرس مشوارنا في سلسلة تنصيب شير بوينت من خلال التعرّف على كيفيّة إجراء النسخ الاحتياطي backup والاستعادة restore لموقع الويب Hsoub Inc الذي أنشأناه مسبقًا في هذا الدرس. يُعتبر النسخ الاحتياطي من أهم المهام التي يجب إجراؤها بصفة دورية لمواقع شير بوينت. حيث أنّ بيانات الشركة ستكون ضمن هذه المواقع. وبالتالي فمن الحكمة إجراء النسخ الاحتياطي بشكل دوري، والاحتفاظ بالملفات الناتجة عن هذه العمليّة في مكان آمن. فعند حدوث أيّ مشكلة، يمكن عندها إجراء عملية استعادة ليعود الموقع (أو المواقع) كما كان عند إجراء عملية النسخ الاحتياطي. توجد العديد من أنواع النسخ الاحتياطي في شير بوينت. وقد تُصاب بالدهشة من كثرة تنوّعها وأساليبها. سنعتمد في هذا الدرس الشكل الذي أجده جيّدًا ومقبولًا وهو إجراء النسخ الاحتياطي على مستوى موقع الويب. ولكن قبل الدخول في التفاصيل دعونا ندخل إلى الصفحة الخاصة بمركز إدارة شير بوينت ونرى كيف يحتفظ شير بوينت بالمواقع المسجّلة ضمنه. الوصول إلى مواقع ويب من خلال مركز إدارة شير بوينت انتقل إلى خادوم التطبيق والويب HSB-SP. انقر زر ابدأ لتظهر صفحة البداية، ثم اكتب مباشرةً العبارة التالية: SharePoint 2013 Central Administration سيبحث الويندوز عن هذا التطبيق ويُظهره لك في قائمة النتائج. شغّل هذا التطبيق، سيطلب منك ويندوز أولًا السماح بتشغيله. اقبل ذلك، ستلاحظ أنّ متصفّح الإنترنت Internet Explorer سيعمل ويطلب منك تسجيل الدخول. استخدم الحساب HSOUB-SP\SPAdmin لتسجيل الدخول. بعد تسجيل الدخول ستصل إلى الصفحة الرئيسيّة لمركز إدارة شير بوينت. من المهم أن نعلم أنّ هذا المركز هو بحدّ ذاته عبارة عن موقع ويب، ولكنّه مخصّص للأمور الإداريّة والتحكّم ببقيّة المواقع على شير بوينت من خلال إنشاء مواقع جديدة، أو التحكّم بالمواقع الموجودة مسبقًا، بالإضافة إلى الكثير من المهام الأخرى التي تتحكّم تقريبًا بجميع إعدادات نسخة شير بوينت. أمر آخر من المفيد ملاحظته، وهو أنّ العنوان الذي يظهر في شريط العنوان للمتصفّح يكون على الشكل التالي: http://hsb-sp:44444 وهو يشبه العنوان الذي كنّا نستخدمه للدخول إلى موقعنا الذي أنشأناه مسبقًا باستثناء أنّنا كنّا لا نستخدم رقم المنفذ 44444 الموجود بعد اسم الموقع. عندما نستخدم العنوان http://hsb-sp للوصول إلى موقعنا ففي الواقع نحن نستخدم المنفذ 80 بشكل ضمنيّ. إذًا فاستخدام المنفذ 44444 مع العنوان http://hsb-sp سينقلنا إلى صفحة الإدارة. أمّا استخدام المنفذ 80 مع العنوان http://hsb-sp فسينقلنا إلى موقع ويب الذي أنشأناه مسبقًا. انقر إدارة تطبيقات الويب Manage web applications كما يظهر في الشكل السابق. سينتقل بك المتصفّح إلى صفحة تعرض جميع تطبيقات الويب المتوفّرة حاليًا. انظر الشكل التالي: نلاحظ أنّه يوجد لدينا تطبيقا ويب. الأوّل هو التطبيق التجريبي الذي أنشأناه مسبقًا ويمكن الدخول إليه عن طريق المنفذ 80 كما أسلفنا. أمّا التطبيق الثاني فهو التطبيق الحالي الذي نستخدمه وهو مخصّص للإدارة ويمكن الوصول إليه عن طريق المنفذ 44444. من الضروري أن نفهم هنا أنّ تطبيق الويب الذي نراه في القائمة السابقة هو نفسه موجود ضمن تطبيق خادوم الويب IIS. وأيضًا أنّه من الممكن ربط أكثر من موقع واحد بتطبيق ويب. في حالتنا هذه ربطنا موقعنا التجريبي بتطبيق واحد وهذا أفضل من ناحية الأداء. أخذ نسخة احتياطيّة عن موقع ويب توجد طريقة جاهزة ضمن الموقع الخاص بإدارة شير بوينت Central Administration لأخذ نسخ احتياطيّة متعدّدة الإمكانيات والخيارات. ولكنّني آثرت استخدام تطبيق Power Shell بهدف الحصول على هذه النسخة الاحتياطيّة. لأنّني وجدته أسهل وعمليّ أكثر، وأقل عرضةً للمشاكل والأخطاء. في الحقيقة يمكن ضبط كل قطعة في شير بوينت باستخدام Power Shell بمفرده أي دون استخدام تطبيق الويب Central Administration. ولكن استخدم Power Shell يُعتبر متقدّما، ويحتاج إلى دراسة كاملة. انقر زر ابدأ لتصل إلى صفحة البداية ثم اكتب مباشرةً العبارة التالية: SharePoint 2013 Management Shell سيبحث ويندوز عن هذا التطبيق ويُظهره كنتيجة بحث. شغّل هذا التطبيق لتحصل على نافذة موجّه أوامر تقليديّة كما في الشكل التالي: لاحظ اسم المستخدم SPAdmin ضمن موجّه الأوامر. اكتب السطر التالي مباشرةً: Backup-SPSite -Identity "http://hsb-sp" -Path F:\Backup\sp20160817.bak يتكوّن هذا السطر من المقاطع التالية: Backup-SPSite وهو الأمر الذي سيعمل على أخذ النسخة الاحتياطيّة. Identity: وهو وسيط يتمّ من خلاله تحديد عنوان الموقع الذي نريد إجراء النسخ الاحتياطي له. في حالتنا هذه نريد إجراء النسخ الاحتياطي للموقع http://hsb-sp. Path: وهو المسار مع اسم الملف الذي نرغب بتخزين الملف الاحتياطي ضمنه. لقد اخترت المسار F:\Backup وهو قد يكون مسار لقرص صلب خارجي مثلًا. بالنسبة إلى اسم الملف فيمكنك استخدام أي اسم ترغب به على أن يكون له الامتداد bak. بالنسبة إليّ أحب أن أسمّي ملفات النسخ الاحتياطي بالشكل التالي: تبدأ بالحرفين sp للإشارة إلى شير بوينت، ثم العام (أربع خانات) يليه الشهر (خانتان) يليه اليوم (خانتان) ثم يأتي امتداد الملف بالطبع. يظهر من الأمر السابق أنّني أجريت النسخ الاحتياطي بتاريخ 17/08/2016. انظر الشكل التالي لترى كيف يبدو أمر النسخ الاحتياطي قبل تنفيذه: اضغط المفتاح Enter ليبدأ النسخ الاحتياطي بالعمل. سيأخذ ذلك بعض الوقت ويتعلّق بالطبع بحجم الموقع وسرعة النسخ إلى القرص الصلب. بعد الانتهاء سيعود موجّه الأوامر إلى وضعية إدخال الأوامر. تهانينا! لقد نفذت أوّل نسخ احتياطيّ لك في شير بوينت. إجراء عملية استعادة لنسخة احتياطية لموقع ويب عمليّة الاستعادة بسيطة كما هي عمليّة أخذ النسخة الاحتياطيّة. لكن لكي نجري تجربة عمليّة حول هذا الموضوع علينا أولًا أن ننشئ تطبيقًا جديدًا لنعمل على استعادة موقع Hsoub Inc إليه. إنشاء تطبيق ويب جديد انتقل إلى إدارة تطبيقات الويب Manage Web Applications كما فعلنا في الفقرة السابقة. ثم انقر الأيقونة التي تحمل الاسم New من الزاوية اليسرى العليا. ستحصل على شكل شبيه بما يلي. احرص أن تكون الاعدادات كما تظهر في الشكل، مع الانتباه إلى جعل رقم المنفذ 22222. انقر زر OK ليعمل شير بوينت على إنشاء هذا التطبيق. سيستغرق ذلك القليل من الوقت. استعادة النسخة الاحتياطيّة إلى تطبيق الويب الجديد انتقل إلى التطبيق SharePoint 2013 Management Shell كما فعلنا في فقرة أخذ النسخة الاحتياطيّة، ثم اكتب السطر التالي ضمن موجّه الأوامر: Restore-SPSite -Identity "http://hsb-sp:22222" -Path F:\Backup\sp20160817.bak يتكوّن هذا السطر من المقاطع التالية: Restore-SPSite وهو الأمر الذي سيعمل على استعادة النسخة الاحتياطيّة. Identity: وهو وسيط يتمّ من خلاله تحديد عنوان الموقع الذي نريد استعادة النسخة الاحتياطية إليه. في حالتنا هذه نريد الاستعادة إلى الموقع http://hsb-sp:22222. Path: وهو المسار مع اسم الملف الذي نرغب بالاستعادة منه. اضغط المفتاح Enter لتبدأ عملية الاستعادة. سيطلب منك البرنامج رسالة تأكيد. اكتب Y (أي Yes) ثم اضغط Enter. سيستغرق الأمر بعض الوقت. بعد أن تنتهي هذه العمليّة ويعود موجّه الأوامر إلى حالته الطبيعيّة. يمكنك الانتقال إلى الحاسوب المكتبي ومحاولة الوصول إلى الموقع http://hsb-sp:22222. بعد تسجيل الدخول وظهور الصفحة الرئيسيّة، انتقل إلى محتويات الموقع Site contents ستجد أنّ التطبيقين اللذين أضفناهما إلى الموقع الأصلي http://hsb-sp في درس سابق، قد أصبحا موجودين هنا. مما يدل على نجاح عمليّة الاستعادة. الخلاصة تعرّفنا في هذا الدرس على كيفيّة أخذ نسخة احتياطيّة backup لموقع ويب، ومن ثمّ استعادة هذه النسخة إلى تطبيق ويب جديد. هذا الدرس هو الخاتم لهذه السلسلة كما أشرنا. في الحقيقة يوجد الكثير مما يمكن قوله، فالموضوع كبير ومتشعّب. لقد حاولت من خلال هذه السلسلة تقديم أسلوب بسيط وعملي لإنشاء مزرعة شير بوينت قابلة للعمل. أرجو أن يكون الهدف قد تحقّق من هذه السلسلة، كما أرجو التوفيق لجميع القرّاء.
  5. قبل أن تبدأ بإضافة الأصناف والمنتجات، وقبل أن تشرع عمومًا ببدء نشاطك التجاري، ينبغي أن تقوم بتجهيز الإعدادات الأساسيّة لمتجرك الإلكتروني، والتي تشتمل على معلومات وبيانات خاصّة بهذا المتجر. كمثال على ذلك البيانات المتعلّقة باسم المتجر وعنوانه ورقم الهاتف والبريد الإلكتروني. ستظهر هذه المعلومات على جميع رسائل البريد الإلكتروني الصادر من ماجنتو، وكذلك على الفواتير وعلى التعاملات المالية مع زبائنك بشكل عام. سنتحدّث في هذا الدرس وفي الدرس التالي من سلسلة إعداد ماجنتو عن كيفيّة ضبط مثل هذه الإعدادات بالشكل الصحيح. عليك أولًا تسجيل الدخول كمدير للنظام عن طريق الرابط http://ubuntu/magento/admin كما مرّ معنا. بعد ذلك ومن القائمة الجانبيّة الخاصّة بمدير النظام والتي تظهر في الجهة اليسرى انقر STORES. ستظهر نافذة منبثقة اختر منها التكوين Configuration (تحت بند الإعدادات Settings). وبعد أن تظهر الصفحة المطلوبة اختر من قائمة الخيارات الجانبيّة General (تحت البند GENERAL أيضًا). بيانات المتجر الأساسيّة Store Information تشتمل هذه البيانات على اسم المتجر ورقم الهاتف وساعات العمل والدولة والمدينة التي يتبع لها. وأيضًا على العنوان التفصيلي له. كما يوجد حقل يُعبّر عن معرّف ضريبة القيمة المُضافة VAT التي يتم تطبيقها في العديد من دول العالم، مثل دول الاتحاد الأوربي والولايات المتحدة وغيرها. انظر الشكل التالي: اتبع الخطوات التالية لتتمكن من ضبط هذه الإعدادات: انشر بند بيانات المتجر Store Information في الأعلى وابدأ بتعبئة البيانات على الشكل التالي: اسم المتجر Store Name الذي سيُستَخدم في جميع التعاملات. رقم الهاتف الخاص بالمتجر Store Phone Number. بالنسبة لحقل ساعات العمل Store Hours of Operation أدخل ساعات العمل في المتجر مثل: "من السبت إلى الخميس 08:00 – 16:00"، وذلك في حال كان المتجر الخاص بك له وجود فيزيائي حقيقي. أمّا في حال كان متجرك الكترونيًّا فحسب ويمكنك تلقي طلبات الشراء والتعاملات الخاصة بها في أيّ وقت فلا بأس من ترك هذا الحقل فارغًا. اختر الدولة Country التي يعمل ضمنها متجرك. اختر المنطقة – المدينة Region/State. ثم يمكنك إدخال العنوان الخاص بك على حقلي العنوان المتتاليين: Store Address و Store Address Line 2. إذا كنت تمتلك رقم VAT الذي تحدثنا عنه قبل قليل فيمكنك إدخاله ضمن الحقل VAT Number، كما ويمكنك التحقّق من صحته بالضغط على زر التحقّق Validate VAT Number في الأسفل مباشرةً. عند الانتهاء انقر زر الحفظ Save Config الموجود في الزاوية العليا اليمنى. الخيارات الإقليميّة Locale Options يمكن من خلال الخيارات الإقليميّة تحديد إعدادات مثل لغة البلد والمنطقة الزمنيّة وأيّام العمل الخاصّة بالمنطقة أو البلد الذي تعمل فيه. انظر الشكل التالي: لضبط هذه الإعدادات عليك بالخطوات التالية: انشر بند الخيارات الإقليميّة Locale Options. اختر المنطقة الزمنية Timezone الخاصّة بك من القائمة، ثم اختر من القائمة Locale البلد واللغة الخاصّة به. وإذا أردت تغيير الإعدادات الافتراضيّة الموجودة بجوار كلّ من واحدة الأوزان Weight Unit واليوم الأوّل بالأسبوع First Day of the Week وأيّام العطلة الأسبوعيّة Weekend Days فعليك إزالة علامة الاختيار من صناديق الاختيار: Use system value الموجودة على يمين كلّ منها. وإلّا سيستخدم ماجنتو الإعدادات الافتراضيّة. عند الانتهاء انقر زر الحفظ Save Config الموجود في الزاوية العليا اليمنى. خيارات الدول Country Options يمكن من خلال هذا القسم تحديد الدولة التي يتبع لها هذا المتجر بصورة عامة. بالإضافة إلى خيارات مثل ضرورة تحديد الرمز البريد Zip/Postal لبعض الدول، وعدم ضرورته بالنسبة لدول أخرى، كما يمكن تحديد الدول التي تقبل البيع إلى الزبائن القادمين منها، كما يمكن تحديد الدول التي ترغب اعتبارها من دول الاتحاد الأوروبي European Union Countries، أو بمعنى آخر ترغب بمعاملتها بنفس معاملة دول الاتحاد. انظر إلى الشكل التالي: يمكنك ضبط الإعدادات السابقة من خلال الخطوات البسيطة التالية: انشر البند خيارات الدول Country Options، ثم قُم بما يلي: اختر الدولة الافتراضيّة Default Country حيث يوجد عملك. من قائمة الدول المسموحة Allow Countries اختر جميع الدول التي ترغب باستقبال طلبات الشراء منها فقط. لاحظ أنّه بشكل افتراضيّ ستكون جميع الدول مختارة، لذلك ستحتاج أولًا إلى الغاء الاختيار من صندوق الاختيار User system value المجاور إذا رغبت باختيار دول محدّدة. يمكنك اختيار أكثر من دولة بنفس الوقت اضغط المفتاح Ctrl بينما تنقر على الدول التي ترغب بها. توجد قائمة اسمها Zip/Postal Code is Optional for وهي مفيدة في تحديد الدول التي لا يحتاج مواطنوها (الزبائن) إلى أن يكون الرمز البريدي جزءًا من العنوان Address الخاص بهم عند الشراء. بالنسبة لقائمة دول الاتحاد الأوربي European Union Countries، يمكنك اختيار أي دولة كي تعاملها كدول الاتحاد الأوروبي. لاحظ بأنّك جميع دول الاتحاد الأوروبي مختارة بشكل افتراضي. القائمة الأخيرة Top Destinations تمثّل الدول الرئيسيّة التي تستهدفها بمبيعاتك. عند الانتهاء انقر زر الحفظ Save Config الموجود في الزاوية العليا اليمنى. خيارات المدن State Options يختلف معنى كلمة State باختلاف الدول. فهناك من يعتبرها ولاية، أو محافظة، أو حتى مجرّد مدينة. تبرز الحاجة إلى وضع خيارات للمدن، في أنّ بعض الدول تتطلّب أن يكون اسم المدينة كجزء من عنوان الشخص، في حين أنّ دولًا أخرى لا تتطلّب ذلك. لهذا السبب يمنح ماجنتو الخيار في إلزام الزبون بذكر المدينة (الولاية – المحافظة) أثناء عمليّة الشراء، أو أن يُعفى من ذلك إذا كانت الدولة التي قَدِم منها لا تتطلّب ذلك. انظر إلى الشكل التالي: لضبط خيارات المدن اتبع الخطوات البسيطة التالية: انشر البند State Options ثم نفّذ ما يلي: من قائمة State is required اختر كل دولة يكون ذكر المدينة (الولاية – المحافظة) ضروريًّا. ويمكنك بالطبع اختيار أكثر من دولة بضغط المفتاح Ctrl أثناء النقر. اضبط القائمة المنسدلة Allow to Choose State if It is Optional for Country لإتاحة الإمكانيّة لاختيار المدينة إذا كانت غير ضروريّة بالنسبة للدولة، وأحب الزبون أن يُحدّد مدينته رغم ذلك. عند الانتهاء انقر زر الحفظ Save Config الموجود في الزاوية العليا اليمنى. الخلاصة تناولنا في هذا الدرس كيفيّة ضبط بعض الإعدادات والخيارات اللازمة لتشغيل ماجنتو بالشكل الصحيح، وذلك من خلال ضبط بيانات المتجر الأساسيّة، والخيارات الإقليميّة، وخيارات الدول، وخيارات المدن. سنكمل عمليّة الضبط هذه في الدرس القادم حيث سنتعرّف على كيفيّة ضبط إعدادات البريد الإلكتروني وموقع العمل والعملات.
  6. سنتابع عملنا في سلسلة تعلّم تطوير تطبيقات أندرويد باستخدام Xamarin.Forms مع الجزء الثاني للعناصر المرئيّة الشائعة في Xamarin.Forms. تناولنا في الجزء الأوّل بعضًا من هذه العناصر حيث تحدثنا عن عناصر القائمة ListView وحقل الإدخال Entry وعنصر تحويل الحالة المنطقيّة Switch كما تحدثنا أيضًا عن عنصر المنزلق Slider. سنتحدّث في هذا الدرس عن عنصر DatePicker الذي يسمح باختيار تاريخ محدّد بطريقة جميلة، كما سنتحدّث عن عنصر الاختيار الخطوي Stepper الذي يشبه عنصر المنزلق Slider، وأيضًا عنصر الإطار Frame الذي يُستَخدم لتعزيز الناحية الجمالية لواجهة المستخدم. سنتناول تطبيقين عمليّين لتوضيح عمل هذه العناصر. تطبيق التحكّم بعرض الحدود وهو تطبيق بسيط للغاية، الغرض منه توضيح كيفيّة استخدام العنصر Stepper. يمتلك هذا العنصر بنية برمجيّة شبيهة بعنصر المنزلق Slider الذي تحدثنا عنه في الدرس السابق، رغم أنّه يختلف عنه من الناحية الشكليّة، فهذا العنصر يتكوّن من زرّين متجاورين يظهر عليهما النصّان "+" و "-". توجد ميزة إضافية في هذا العنصر تتمثّل في وجود خاصيّة اسمها Increment قيمتها الافتراضيّة 1. وتُستخدم لتحديد مقدار الزيادة أو النقصان عند كل نقرة على الزرّين السابقين كما سنرى بعد قليل. يُستخدم عنصر Stepper غالبًا لاختيار قيم عدديّة صحيحة (بدون فاصلة عشريّة) مع أنّه من الممكن استخدام القيم العشرية معه. أنشئ مشروعًا من النوع Blank App (Xamarin.Forms Portable) وسمّه StepperDemoApp، ثم أبق فقط على المشروعين StepperDemoApp (Portable) و StepperDemoApp.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك أضف صفحة محتوى جديدة كما فعلنا في هذا الدرس وسمّها StepperDemoPage. احرص على أن تكون محتويات الملف StepperDemoPage.cs على الشكل التالي: 1 using Xamarin.Forms; 2 3 namespace StepperDemoApp 4 { 5 public class StepperDemoPage : ContentPage 6 { 7 public StepperDemoPage() 8 { 9 Button btnDemo = new Button 10 { 11 Text = "اختبار سماكة حدود الزر", 12 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)), 13 BorderColor = Color.FromHex("C0C0C0"), 14 BackgroundColor = Color.FromHex("404040"), 15 HorizontalOptions = LayoutOptions.Center, 16 VerticalOptions = LayoutOptions.CenterAndExpand 17 }; 18 19 Label lblCurrentBorderWidth = new Label 20 { 21 Text = "السماكة الحالية: 0", 22 HorizontalOptions = LayoutOptions.FillAndExpand, 23 HorizontalTextAlignment = TextAlignment.Center 24 }; 25 26 Stepper stepper = new Stepper 27 { 28 HorizontalOptions = LayoutOptions.Center, 29 Maximum = 10, 30 Minimum = 0 31 }; 32 stepper.ValueChanged += (s, e) => 33 { 34 btnDemo.BorderWidth = stepper.Value; 35 36 lblCurrentBorderWidth.Text = "السماكة الحالية: " + stepper.Value.ToString(); 37 }; 38 39 StackLayout changeBorderWidthSLayout = new StackLayout 40 { 41 VerticalOptions = LayoutOptions.CenterAndExpand, 42 Children = 43 { 44 lblCurrentBorderWidth, 45 stepper 46 } 47 }; 48 49 Content = new StackLayout 50 { 51 Children = 52 { 53 btnDemo, 54 changeBorderWidthSLayout 55 } 56 }; 57 } 58 } 59 } انتقل إلى الملف App.cs واحرص على أن تكون بانية الصنف App على الشكل التالي: public App() { // The root page of your application MainPage = new StepperDemoPage(); } نفّذ البرنامج باستخدام F5، ثم تفاعل مع التطبيق بنقر الزرّين "+" و "-" لتحصل على شكل شبيه بما يلي: يتكوّن التطبيق من زر في الأعلى يحوي النص "اختبار سماكة الحدود" بالإضافة إلى عنصر Stepper يسمح لك بضبط قيمة الحدود لهذا الزر، وأيضًا توجد لصيقة صغيرة تعلو العنصر Stepper وظيفتها عرض القيمة الحالية المختارة لسماكة حد الزر. صرّحنا في الأسطر من 9 حتى 17 عن عنصر زر جديد وأسندناه إلى المتغيّر btnDemo. لقد جعلنا الخط المُستخدَم في هذا الزر كبيرًا، وأيضًا ضبطنا لون الحد عن طريق الخاصيّة BorderColor (السطر 13) ليكون هذا اللون "C0C0C0" وهي قيمة ست عشرية تتكوّن من 3 مكوّنات لونية هي من اليسار إلى اليمين: الأحمر والأخضر والأزرق بحيث يأخذ كل مكوّن لوني محرفين. فمن خلال القيمة "C0C0C0" سيأخذ كل من الأحمر والأخضر والأزرق القيمة C0 وهي حالة خاصّة بالطبع وسنحصل من خلالها على أحد تدرّجات اللون الرمادي. بنفس الأسلوب قمنا بضبط لون الخلفيّة لهذا الزر من خلال الخاصيّة BackgroundColor (السطر 14) ليكون "404040" وهو أيضًا أحد تدرّجات الرمادي. يمكنك اختيار أيّ لون ترغبه. سنضع كلًّا من اللصيقة المسؤولة عن عرض قيمة الحد الحالي lblCurrentBorderWidth (الأسطر من 19 حتى 24) وأيضًا عنصر الاختيار الخطوي stepper (الأسطر من 26 حتى 31) ضمن مخطّط مكدّس ليظهرا متجاورين رأسيًّا. مخطّط المكدّس هذا مصرّح عنه في الأسطر من 39 حتى 47 عن طريق المتغيّر changeBorderWidthSLayout. لاحظ أخيرًا أنّنا قد صرّحنا عن معالج الحدث ValueChanged للعنصر stepper بصورة مماثلة تمامًا لما فعلناه مع عنصر المنزلق Slider في الدرس السابق، وذلك في الأسطر من 32 حتى 37 لكي نعالج نقرات المستخدم على هذا العنصر، وبنفس الوقت نحدّث قيمة اللصيقة لتعرض السماكة الحاليّة لحدود الزر (السطر 36). تطبيق حساب العمر بالأيّام نحتاج لإنجاز هذا التطبيق إلى عنصر DatePicker لاختيار تاريخ الولادة، وعنصر Frame لإكساب صفة جمالية على التطبيق كما سنرى، كما سنحتاج إلى زر لإجراء عمليّة الحساب ولصيقة لعرض النتيجة. سيتم حساب العمر الحالي بالأيّام من تاريخ الولادة حتى تاريخ اليوم. أنشئ مشروعًا من النوع Blank App (Xamarin.Forms Portable) وسمّه AgeCalculatorApp، ثم أبق فقط على المشروعين AgeCalculatorApp (Portable) وAgeCalculatorApp.Droid. بعد ذلك أضف صفحة محتوى جديدة وسمّها AgeCalculatorPage. احرص على أن تكون محتويات الملف AgeCalculatorPage.cs على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace AgeCalculatorApp 5 { 6 public class AgeCalculatorPage : ContentPage 7 { 8 public AgeCalculatorPage() 9 { 10 Frame frmWelcome = new Frame 11 { 12 VerticalOptions = LayoutOptions.Start, 13 Padding = new Thickness(15), 14 BackgroundColor = Color.FromRgba(Color.Accent.R, 15 Color.Accent.G, 16 Color.Accent.B, 17 0.2), 18 OutlineColor = Color.Accent, 19 Content = new Label 20 { 21 Text = "تطبيق حساب العمر بالأيّام", 22 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), 23 TextColor = Color.Aqua, 24 HorizontalTextAlignment = TextAlignment.Center 25 } 26 }; 27 28 29 DatePicker birthDate = new DatePicker 30 { 31 VerticalOptions = LayoutOptions.Start, 32 HorizontalOptions = LayoutOptions.FillAndExpand, 33 34 }; 35 36 Label lblCurrentDate = new Label 37 { 38 Text = DateTime.Now.ToString("dd/MM/yyyy"), 39 VerticalOptions = LayoutOptions.Start, 40 HorizontalOptions = LayoutOptions.FillAndExpand, 41 FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) 42 }; 43 44 Label lblResult = new Label 45 { 46 VerticalOptions = LayoutOptions.FillAndExpand, 47 FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), 48 VerticalTextAlignment = TextAlignment.Start, 49 HorizontalTextAlignment = TextAlignment.Center, 50 TextColor = Color.Accent 51 }; 52 53 Button btnCalculate = new Button 54 { 55 Text = "احسب", 56 VerticalOptions = LayoutOptions.Start 57 }; 58 btnCalculate.Clicked += (s, e) => 59 { 60 TimeSpan diff = DateTime.Now - birthDate.Date; 61 62 lblResult.Text = string.Format("عمرك {0} يومًا", diff.Days); 63 }; 64 65 Content = new StackLayout 66 { 67 Children = 68 { 69 frmWelcome, 70 birthDate, 71 lblCurrentDate, 72 btnCalculate, 73 lblResult 74 } 75 }; 76 77 Padding = new Thickness(5); 78 } 79 } 80 } انتقل بعد ذلك إلى الملف App.cs وتأكّد أنّ بانية الصنف App على الشكل التالي: public App() { // The root page of your application MainPage = new AgeCalculatorPage(); } نفّذ البرنامج باستخدام F5 لتحصل على شكل شبيه بما يلي: اختر تاريخ مولدك (الميلادي) ولاحظ الصندوق الذي يملأ حيزًا لا بأس به من الشاشة، وكف يسمح لك باختيار التاريخ بصورة جميلة. بعد أن تختار التاريخ المناسب لك، انقر زر "احسب" لتحصل على عمرك بالأيّام من الأسفل. أنشأنا في الأسطر من 10 حتى 26 كائن جديد من الصنف Frame وأسندناه إلى المتغيّر frmWelcome. تُعتبر عناصر Frame ذات بنية مستطيلة تُستخدم لأغراض تحسين واجهة المستخدم. يعرض عنصر Frame حدًّا مستطيلًا يحيط بمحتوى معيّن. وإليك الآن بعض الملاحظات البسيطة حوله: يحتوي عنصر الإطار Frame على الخاصيّة Content التي يمكن أن نُسند إليها أي عنصر آخر، أو من الممكن أن نسند إليها مخطّط مكدّس يحتوي بدوره على العديد من العناصر الأخرى. في مثالنا هذا أسندنا إلى هذه الخاصيّة كائن لصيقة Label (السطر 19) يحتوي على عنوان التطبيق. يضم عنصر الإطار أيضًا الخاصيّة Padding لإضافة حشوة داخليّة بين حدود الإطار والمحتوى الذي يقع ضمنه (السطر 13). يمكن التحكّم بلون حد الإطار من خلال الخاصيّة OutlineColor (السطر 18) حيث عملنا في هذا التطبيق على إسناد اللون Color.Accent إليه. الخاصيّة BackgroundColor للإطار تتحكّم بلون الخلفيّة كما نعلم، ولكن لاحظ الشكل الجديد الذي استخدمناه في إسناد اللون لها هذه المرّة (الأسطر من 14 حتى 17). استخدمنا هذه المرّة التابع Color.FromRgba الذي يُرجع كائن من النوع Color اعتبارًا من القيم اللونية RGB ومقدار الشفافيّة A الممرّرة إليه. ونستفيد من هذا الشكل في الحصول على ألوان مخصّصة أكثر. أحببت أن أستخدم اللون Accent ولكن بشكل شفّاف لذلك حصلت على المركّبات اللونية له باستخدام Color.Accent.R للأحمر وColor.Accent.G للأخضر وColor.Accent.B للأزرق، ومرّرتها إلى التابع Color.FromRgba بنفس الترتيب السابق. أمّا الوسيط الأخير فقد مرّرت إليه القيمة 0.2 ليظهر اللون شفَّافًا. قيمة الوسيط الأخير (قيمة A) تتراوح بين 0 (شفّاف تمامًا) و1 (معتم تمامًا). بالنسبة للعنصر DatePicker فيُستخدم كما أشرنا لاختيار تاريخ محدّد بصورة جميلة. صرّحنا عنه في الأسطر من 29 حتى 34 وأسندنا الكائن الناتج إلى المتغيّر birthDate ويُعبّر عن تاريخ الولادة. إليك بعض الملاحظات المتعلّقة بهذا العنصر: يمكن الوصول إلى قيمة التاريخ الموجودة حاليًا ضمن هذا العنصر عن طريق الخاصيّة Date له كما سنرى بعد قليل. نوع هذه الخاصيّة مألوف وهو DateTime. توجد خاصيّتان مفيدتان في بعض الأحيان ولكن لم نستخدمهما في هذا التطبيق، وهما MinimumDate لتحديد التاريخ الأدنى الذي لا يمكن الاختيار قبله، و MaximumDate لتحديد التاريخ الأعلى الذي لا يمكن الاختيار بعده. خصّصنا لصيقة بسيطة لعرض النتائج (الأسطر من 44 حتى 51) وصرّحنا أيضًا عن الزر الخاص بإجراء عمليّة الحساب (الأسطر من 53 حتى 57) وأسندناه إلى المتغيّر btnCalculate. أسندنا معالج حدث النقر لهذا الزر في الأسطر من 58 حتى 63 على شكل تعبير Lambda. يحتوي هذا المعالج على شيفرة برمجيّة بسيطة للغاية، فهو يقرأ قيمة الخاصيّة Date من المتغيّر birthDate ويطرحها من التاريخ الحالي ويُسند النتيجة إلى المتغيّر diff الذي سيكون في هذه الحالة من النوع TimeSpan وهي بنية يمكن استخدامها لقياس فترات زمنيّة مُحدّدة (السطر 60) في نهاية الأمر نستخدم الخاصيّة Days من المتغيّر diff للحصول على الفترة الزمنية المطلوبة والتي تمثّل عمر الشخص بالأيّام. قد لا يبدو مظهر هذا التطبيق احترافيًّا كفاية. ولكنّنا سنتعلّم من خلال الدروس القادمة كيفيّة تحسين واجهة المستخدم وجعلها جذّابة من خلال تقنيّات بسيطة نسبيًّا. الخلاصة تعاملنا في هذا الدرس مع بعض العناصر المرئيّة المستخدمة في Xamarin.Forms. توجد بعض العناصر الأخرى التي لم نتحدّث عنها، أو التي لم نعطها حقّها في الحديث عنها. في الواقع أفضّل تأجيل ذلك إلى دروس قادمة لأنّ استخدام بعض هذه العناصر يتطلّب مفاهيم متقدّمة نسبيًّا في Xamarin.Forms. سنبدأ في الدرس التالي تعلّم كيفيّة بناء الواجهات باستخدام رُماز XAML. حيث سنتمكّن من فصل واجهة المستخدم عن الشيفرة البرمجيّة التي تتفاعل معها.
  7. سنهتمّ في هذا الدرس من سلسلة تعلّم تطوير تطبيقات أندرويد باستخدام Xamarin.Forms بالعناصر المرئيّة الشائعة في Xamarin.Forms. يحتاج أيّ تطبيق بصورة عامة إلى العديد من العناصر المرئيّة التي تظهر على الشاشة بحيث توفّر معلومات معيّنة للمستخدم أو تسمح له بالتفاعل معها. يرث أيّ عنصر مرئيّ في Xamarin.Forms من الصنف VisualElement، سنتحدّث في هذا الدرس عن العنصر Entry وهو عبارة عن حقل خاص بالإدخال يستقبل مُدخلات المستخدم. وأيضًا عن العنصر ListView وهو عنصر القائمة ووظيفته عرض العناصر على شكل قائمة قابلة للتمرير. كما سنتحدّث عن العنصر Switch وهو عنصر تحويل الحالة المنطقيّة ويحمل إحدى قيمتين true أو false. وأخيرًا سنتحدّث عن عنصر المنزلق Slider ووظيفته السماح للمستخدم باختيار قيمة ضمن مجال محدّد عن طريق السحب. سنتناول تطبيقين بسيطين لفهم كيفيّة التعامل مع هذه العناصر. تطبيق إدخال النصوص فكرة هذا التطبيق بسيطة، حيث سنتعلّم كيف سنستخدم العنصر Entry للسماح للمستخدم بإدخال قيم نصيّة، ستُضاف هذه القيم إلى عنصر القائمة ListView. أنشئ مشروعًا من النوع Blank App (Xamarin.Forms Portable) وسمّه TextEntriesApp، ثم أبق فقط على المشروعين TextEntriesApp (Portable) و TextEntriesApp.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك أضف صفحة محتوى جديدة كما فعلنا في هذا الدرس وسمّها TextEntriesPage. احرص على أن تكون محتويات الملف TextEntriesPage.cs على الشكل التالي: 1 using System.Collections.Generic; 2 using Xamarin.Forms; 3 4 namespace TextEntriesApp 5 { 6 public class TextEntriesPage : ContentPage 7 { 8 public TextEntriesPage() 9 { 10 List<string> entries = new List<string>(); 11 12 Entry txtInput = new Entry 13 { 14 HorizontalOptions = LayoutOptions.FillAndExpand, 15 HorizontalTextAlignment = TextAlignment.End, 16 Placeholder = "أدخل كلمة", 17 FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) 18 }; 19 20 Button btnAddEntry = new Button 21 { 22 Text = "+", 23 HorizontalOptions = LayoutOptions.Start 24 }; 25 btnAddEntry.Clicked += (s, e) => 26 { 27 entries.Add(txtInput.Text); 28 29 txtInput.Text = ""; 30 txtInput.Focus(); 31 }; 32 33 StackLayout inputAreaSLayout = new StackLayout 34 { 35 Orientation = StackOrientation.Horizontal, 36 VerticalOptions = LayoutOptions.Start, 37 Children = 38 { 39 btnAddEntry, 40 txtInput 41 } 42 }; 43 44 ListView lstEntries = new ListView 45 { 46 ItemsSource = entries, 47 VerticalOptions = LayoutOptions.FillAndExpand 48 }; 49 50 51 Content = new StackLayout 52 { 53 Children = 54 { 55 inputAreaSLayout, 56 lstEntries 57 } 58 }; 59 } 60 } 61 } انتقل إلى الملف App.cs واحرص على أن تكون بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new TextEntriesPage(); } نفّذ البرنامج باستخدام F5 لتحصل على شكل شبيه بما يلي: البرنامج السابق بسيط، اكتب كلمة ثم انقر الزر (+). تابع على هذا المنوال حتى تختفي الكلمات أسفل الشاشة، ثم اسحب بإصبعك على هذه القائمة لتظهر الكلمات المختفية. بدأنا في السطر 10 بالتصريح عن المتغيّر entries من نوع List<string> وأسندنا إليه كائنًا جديدًا. يحتوي هذا المتغيّر على المُدخلات التي يدخلها المستخدم، وذلك عند النقر على الزر (+) عند كل إضافة. سنسند هذا المتغيّر إلى الخاصيّة ItemSource لكائن القائمة ListView كما سنرى بعد قليل. أنشأنا في الأسطر من 12 حتى 18 كائن Entry جديد وأسندناه إلى المتغيّر txtInput. لقد أسندنا القيمة TextAlignment.End للخاصية HorizontalTextAlignment لكي يظهر النص محاذًا نحو اليمين (باعتبار أنّنا نستخدم اللغة العربية في إدخال النصوص)، كما تجدر ملاحظة الخاصيّة الجديدة Placeholder التي تسمح بإظهار علامة مائيّة ضمن كائن Entry لإرشاد المستخدم حول وظيفة حقل الإدخال هذا. توجد أيضًا خاصيّة أخرى اسمها PlaceholderColor للتحكّم بلون خط هذه العلامة المائيّة. يمكن الوصول إلى محتوى حقل الإدخال عن طريق الخاصيّة Text له. قمنا بعد ذلك في الأسطر من 20 حتى 31 بإضافة زر جديد وأسندناه إلى المتغيّر btnAddEntry وجهزّنا معالج حدث النقر الخاص به. بالنسبة لمخطّط المكدّس الذي أنشأناه وأسندناه إلى المتغيّر inputAreaSLayout في الأسطر من 33 حتى 42، فتنحصر وظيفته في جعل كلّ من حقل الإدخال txtInput وزر الإضافة btnAdd يظهران بشكل أفقي متجاور. نأتي الآن إلى أكثر العناصر أهميّةً ألا وهو عنصر القائمة ListView الذي أنشأنا كائنًا جديدًا منه وأسندناه إلى المتغيّر lstEntries في الأسطر من 44 حتى 48. يُستخدَم عنصر القائمة بكثرة في تطبيقات الأجهزة المحمولة، إذ أنّ طبيعته التي تساعد على تمرير محتوياته تجعل منه مناسبًا جدًّا للاستخدام على الشاشات الصغيرة. في الواقع لم نستخدمه في هذا التطبيق إلًّا بأبسط صوره، حيث سنفرد له درسين كاملين للحديث عنه مستقبلًا. يمتلك عنصر القائمة الخاصيّة ItemSource حيث أسندنا لها المتغيّر entries ليقوم بعرض محتوياته ضمنه. وفي كلّ مرّة سنضيف فيها عنصرًا جديدًا على entries سينعكس ذلك على محتويات القائمة أيضًا. ضبطنا أيضًا الخاصيّة VerticalOptions للقائمة بحيث تشغل الحيّز المتبقّي ضمن الصفحة. تطبيق التحكّم بالخط تعتمد فكرة هذا التطبيق على وجود عنصر Switch بالإضافة إلى عنصر Slider وأخيرًا لصيقة تحتوي على نصٍّ بسيط نريد التحكّم بحجمه (عن طريق العنصر Slider) والتحكّم بميول الخط (عن طريق العنصر Switch). أنشئ مشروعًا من النوع Blank App (Xamarin.Forms Portable) وسمّه FontControlApp، ثم أبق فقط على المشروعين FontControlApp (Portable) و FontControlApp.Droid. بعد ذلك أضف صفحة محتوى جديدة وسمّها FontControlPage. احرص على أن تكون محتويات الملف FontControlPage.cs على الشكل التالي: 1 using Xamarin.Forms; 2 3 namespace FontControlApp 4 { 5 public class FontControlPage : ContentPage 6 { 7 private Label lblText; 8 public FontControlPage() 9 { 10 Switch swtItalicFont = new Switch 11 { 12 HorizontalOptions = LayoutOptions.EndAndExpand 13 }; 14 swtItalicFont.Toggled += (s, e) => 15 { 16 if (e.Value) 17 lblText.FontAttributes = FontAttributes.Italic; 18 else 19 lblText.FontAttributes = FontAttributes.None; 20 }; 21 22 StackLayout fontItalicSLayout = new StackLayout 23 { 24 VerticalOptions = LayoutOptions.Start, 25 Orientation = StackOrientation.Horizontal, 26 Children = 27 { 28 swtItalicFont, 29 new Label 30 { 31 Text = "خط مائل", 32 HorizontalOptions = LayoutOptions.End 33 } 34 } 35 }; 36 37 Slider sldFontSize = new Slider 38 { 39 VerticalOptions = LayoutOptions.Center, 40 HorizontalOptions = LayoutOptions.FillAndExpand, 41 Maximum = 40, 42 Minimum = 10, 43 Value = 20 44 }; 45 sldFontSize.ValueChanged += (s, e) => 46 { 47 lblText.FontSize = e.NewValue; 48 }; 49 50 lblText = new Label 51 { 52 VerticalOptions = LayoutOptions.FillAndExpand, 53 VerticalTextAlignment = TextAlignment.Center, 54 HorizontalTextAlignment = TextAlignment.Center, 55 Text="This is a sample text to demonstrate Switch and Slider elements", 56 TextColor = Color.Accent, 57 FontFamily = "Tahoma", 58 FontSize = 20 59 }; 60 61 Content = new StackLayout 62 { 63 Children = 64 { 65 fontItalicSLayout, 66 sldFontSize, 67 lblText 68 } 69 }; 70 71 Padding = new Thickness(5); 72 } 73 } 74 } ثم انتقل إلى الملف App.cs واحرص على أن تكون بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new FontControlPage(); } نفّذ البرنامج باستخدام F5، وتعامل معه قليلًا لتحصل على شكل شبيه بما يلي: صرّحنا في الأسطر من 10 حتى 20 عن كائن Switch جديد وأسندناه إلى المتغيّر swtItalicFont، كما قمنا بإسناد معالج للحدث Toggle كما هو واضح (السطر 14). يتم تفعيل الحدث Toggle عندما ينقر المستخدم على عنصر Switch ليغيّر حالته المنطقيّة من true إلى false أو بالعكس. من الممكن معرفة الوضع الحالي لعنصر Switch بقراءة الخاصيّة Value من الكائن المرّر كوسيط e كما فعلنا في السطر 16. ومن ثمّ نتخذ القرار المناسب في جعل الخط مائلًا أم لا من خلال استخدام المعدودة FontAttributes كما فعلنا في السطرين 17 و19. أنشأنا في الأسطر من 22 حتى 35 مخطّط مكدّس جديد تنحصر وظيفته في جعل عنصر Switch بالإضافة إلى لصيقة صغيرة توضّح عمله بشكل متجاور أفقيًّا وعلى الجهة اليمنى كما هو واضح. بالنسبة لعنصر Slider فقد صرّحنا عنه في الأسطر من 37 حتى 44. حيث أنشأنا كائن جديد من الصنف Slider وأسندناه إلى المتغيّر sldFontSize. تُعتبر عناصر Slider من العناصر المفيدة في واجهة المستخدم، وتُستخدم عادةً لاختيار قيم محصورة ضمن مجال يمكن تحديده مسبقًا. طريقة اختيار هذه القيم هي التي تجعل منها عناصر مميّزة. فأنت تحتاج فقط إلى لمس الدائرة الصغيرة وسحبها إلى اليمين أو اليسار لكي تختار القيمة التي تناسبك. يتم تحديد مجال الاختيار عن طريق الخاصيتين Minimum وMaximum. احرص دومًا على تعيين قيمة الخاصية Maximum قبل Minimum. يمكن قراءة القيمة الحالية ضمن عنصر Slider عن طريق الخاصيّة Value له. لقد عملنا في الشيفرة السابقة على ضبط قيمة افتراضيّة لعنصر Slider وهي 15 (انظر السطر 43) كما جعلنا مجال الاختيار يتراوح بين 10 و40 (السطران 41 و42). الأمر المفيد الآخر في هذا العنصر هو الحدث ValueChanged الذي يُفعّل كلّما تغيّرت القيمة الحالية لعنصر Slider. لقد أسندنا معالج حدث في الأسطر 45 حتى 48 للاستفادة من هذا الحدث، حيث نستفيد من قيمة عنصر Slider مباشرةً في تغيير حجم الخط للنص الموجود ضمن اللصيقة lblText المصرّح عنها في الأسطر من 50 حتى 59. لقد عيّنّا نوع الخط المستخدم في هذه اللصيقة عن طريقة ضبط القيمة "Tahoma" للخاصيّة FontFamily لها (السطر 57). لقد اخترت الخط Tahoma لكي يظهر الخط المائل بشكل واضح. توجد ملاحظة بسيطة أخيرة في الشيفرة السابقة، وهي استخدام شكل جديد لبانية الصنف Thickness (السطر 71) حيث مرّرنا إليها وسيطًا واحدًا فقط بدلًا من أربعة وسائط كما كنّا نفعل من قبل. في الحقيقة عندما تريد تمرير نفس القيمة لكلّ من الجهات الأربع (top، left، right، bottom) فيكفيك أن تمرّر قيمة واحدة فقط تمثّل القيمة الثابتة لهذه الاتجاهات. الخلاصة تحدثنا في هذا الدرس عن بعض العناصر المرئيّة المفيدة في Xamarin.Forms. حيث تناولنا عنصر حقل الإدخال Entry وعنصر تحويل الحالة المنطقية Switch وعنصر المنزلق Slider لاختيار قيمة ضمن مجال محدّد يمكن تعيينه. والعنصر المهم ListView وهو عنصر القائمة. سنتعامل مع هذه العناصر مجّددًا في هذه السلسلة، وسنتوسّع بالحديث عن بعض منها في دروس مخصّصة. سنتناول في الدرس التالي المزيد من هذه العناصر مع تطبيقات ممتعة لها.
  8. بعد انتهائنا من تجهيز مزرعة شير بوينت متكاملة وقابلة للتوسّع. سنتابع العمل في هذه السلسلة في التعرّف على تطبيقين أساسيّين في شير بوينت والتي من الممكن إضافتها إلى موقع الويب الذي أنشأناه في الدرس السابق. سنضيف مكتبة وثائق Document Library وقائمة مخصّصة Custom List. إنشاء مكتبة وثائق Document Library يمتلك أي موقع جديد في شير بوينت في الواقع مكتبة وثائق افتراضيّة. لكنّني آثرت أن أضيف واحدة جديدة لكي نتعلّم كيفيّة ذلك. من الحاسوب المكتبي الذي أعددناه في الدرس السابق، افتح المتصفّح Internet Explorer، ثم اكتب العنوان التالي في شريط العنوان http://hsb-sp ثم اضغط الزر Enter. سيطلب منك المتصفّح تسجيل الدخول إلى صفحة الموقع. استخدم المستخدم HSOUB-SP\SPAdmin وكلمة المرور الخاصّة به كما تعلّمنا في الدرس السابق. بعد تسجيل الدخول ستصل إلى الصفحة الرئيسيّة للموقع. انظر الشكل التالي: انقر على محتويات الموقع Site Contents الموجود في الجهة اليسرى كما يظهر من الشكل السابق، لتحصل على الشكل التالي: إضافة مكتبة وثائق جديدة وتغيير اسمها سنجري الخطوات البسيطة التالية لإتمام هذا الأمر: 1- ستجد العنصر add an app أي إضافة تطبيق. انقره لينتقل المتصفّح إلى نافذة التطبيقات المتاحة كما في الشكل التالي: 2- توجد العديد من التطبيقات التي من الممكن إضافتها لهذا الموقع، يمكنك استعراض هذه الصفحة والاطلاع على هذه التطبيقات. بالنسبة إلينا الآن سنختار التطبيق Document Library أي مكتبة الوثائق. لذلك انقر هذا العنصر بزر الفأرة الأيسر. ستظهر لك نافذة صغيرة تطلب منك إدخال اسم لهذه القائمة. اختر الاسم CompanyDocuments. في الحقيقة سنغيّر هذا الاسم بعد قليل ليكون عربيًّا. أنصح دومًا أن يكون الاسم المبدئي لأيّ تطبيق باللغة الانجليزيّة وألّا تكون هناك أي فراغات بين الكلمات، تمامًا كما فعلنا. انقر بعد ذلك الزر Create ليعمل شير بوينت على إنشاء هذه المكتبة. سيعود المتصفّح بعدها إلى صفحة محتويات الموقع Site Contents ولكنّه في هذه المرّة سيعرض المكتبة التي أنشأناها توًّا مع كلمة new صغيرة للإشارة إلى أنّها جديدة. انظر الشكل التالي: 3- انقر على المكتبة CompanyDocuments بزر الفأرة الأيسر، لينتقل المتصفّح إلى الصفحة التي تعرض محتويات هذه المكتبة التي ستكون فارغة بالطبع. 4- ستجد أعلى النافذة لسان تبويب اسمه LIBRARY انقره لتظهر الخيارات التي تتبع له على شكل شريط Ribbon كما في الشكل التالي: 5- ستلاحظ أيضًا من الشريط الذي سيظهر وجود الأيقونة Library Settings (إعدادات المكتبة) من الجهة اليمنى كما في الشكل السابق. انقرها لينتقل المتصفّح إلى إعدادات المكتبة كما يلي: من هنا يمكنك التحكّم بجميع إعدادات هذه المكتبة. يُظهر الشكل السابق قسمًا من هذه الإعدادات، ويمكنك استخدام شريط التمرير لمشاهدة المزيد منها. 6- اختر البند List name, description and navigation لتغيير اسم المكتبة. سينتقل المتصفّح إلى النافذة الخاصّة بتغيير اسم ووصف هذه المكتبة. زوّد هذه المكتبة باسم جديد وهو: "وثائق الشركة" ثم انقر الزر Save كما في الشكل التالي: سيعود بك المتصفّح بعد ذلك إلى نافذة إعدادات المكتبة. لاحظ كيف تغيّر اسم المكتبة إلى الاسم الجديد "وثائق الشركة". يمكنك العودة إلى المكتبة نفسها بالنقر على اسمها الذي يظهر في هذه النافذة. انظر الشكل التالي: إدراج وثائق ضمن مكتبة الوثائق يمكن في الحقيقة إضافة أيّ نوع من الملفات بما فيها ملفات الوثائق التي هي في الواقع ملفات تطبيقات حزمة office مثل Word وExcel أو من الممكن مثلًا أن تكون ملفات PDF. من الصفحة الرئيسيّة لمكتبة الوثائق ستجد تحت اسم المكتبة رابط اسمه new document or drag files here كما في الشكل التالي: يمكنك سحب أيّ ملف من خارج المتصفّح إلى هذه المنطقة ومن ثمّ إفلاته. سيعمل شير بوينت على رفعه إلى الموقع. انظر إلى الشكل التالي الذي يُظهر سحب ملف وورد وإفلاته ضمن هذه المنطقة، ومن ثمّ ظهوره ضمن محتويات المكتبة بعد انتهاء عملية الرفع. يمكنك النقر على هذا الملف لفتحه ضمن برنامج وورد في حال كان حزمة Office منصّبة على الحاسوب المكتبي. وقد يطلب منك إدخال اسم المستخدم وكلمة المرور. استخدم SPAdmin كما فعلنا في تسجيل الدخول إلى هذا الموقع. إنشاء مكتبة مخصّصة Custom List انتقل إلى محتويات الموقع Site Contents كما تعلّمنا، ثمّ اتبع الخطوات الواردة في الفقرة السابقة: "إضافة مكتبة وثائق جديدة وتغيير اسمها" مع الانتباه إلى بعض الفروقات البسيطة التي تتمثّل في استبدال الكلمة LIBRARY بالكلمة LIST أينما صادفتها. كما يوجد فرق آخر في الخطوة رقم 2 حيث أنّنا سنضيف قائمة مخصّصة Custom List وسنسمّها OrdersList. ويوجد أيضًا فرق آخر في الخطوة رقم 6 حيث سيكون الاسم العربي لهذه القائمة هو "قائمة الطلبيّات". إضافة أعمدة جديدة إلى المكتبة المخصّصة انتقل إلى محتويات القائمة بالنقر على اسمها (كما فعلنا بالنسبة لمكتبة الوثائق). ستحصل على شكل شبيه بما يلي: تُعتبر القائمة في هذه الحالة في طور العرض. ستلاحظ وجود رابط اسمه edit أي تحرير في الأعلى. سيؤدّي نقر هذا الرابط إلى دخول القائمة في طور التحرير. من الممكن في هذا الطور أن نعدّل محتويات القائمة، أو أن نضيف أعمدة جديدة أو نحذف أعمدة موجودة مسبقًا أو حتى أو نغيّر نوع البيانات الموجودة في هذه الأعمدة. انقر الرابط edit للانتقال إلى طور التحرير. انظر الشكل التالي: يؤدي النقر على الزر + إلى إضافة عمود جديد، حيث ستُظهر لك الصفحة قائمة منبثقة تعرض عليك بعض أنواع البيانات التي من الممكن أن يحتويها العمود الجديد علمًا أنّه توجد أنواع إضافية يمكن اختيارها لا تظهر ضمن هذه القائمة. أمّا المؤشّر الصغير الذي يتجه نحو الأسفل والذي يظهر ضمن الدائرة الحمراء بجوار اسم العمود، فيؤدّي نقره إلى ظهور قائمة منسدلة أخرى تحتوي على عدّة أوامر، أوّلها هو تغيير اسم هذا العمود Rename Column. انقر هذا المؤشّر الصغير واختر الأمر الأوّل لتغيير عنوانه ليصبح "العميل". ثم انقر الزر + لإضافة عمود جديد من النوع Text وسمّه "المنتج". أضف عمودًا آخرًا من النوع Date and Time الذي يستوعب التاريخ والوقت، وسمّه "التاريخ". ستحصل على شكل شبيه بما يلي: يمكنك النقر على الرابط Stop الموجود أعلى القائمة للعودة إلى طور العرض، أو أن تبدأ بإضافة العناصر (الأسطر) إلى هذه القائمة مباشرةً عن طريق تعبئة الخلايا التي تظهر في الشكل السابق. يمكنك إضافة السجلات أيضًا من طور العرض عن طريق نقر الرابط new item (عنصر جديد) الذي سيظهر في ذلك الطور. الخلاصة تعرّفنا في هذا الدرس على كيفيّة إضافة تطبيقين من تطبيقات شير بوينت إلى موقعنا التجريبي. يمكن إضافة المزيد من التطبيقات بنفس الأسلوب تقريبًا. في الحقيقة يحتاج الحديث عن تطبيقات شير بوينت إلى سلسلة تعليميّة كاملة. تنحصر وظيفة هذا الدرس في إضافة تطبيقين على سبيل تجريب البيئة الجديدة التي أنشأناها من خلال هذه السلسلة. سنتحدّث في الدرس التالي والأخير من هذه السلسلة عن كيفيّة أخذ نسخة احتياطيّة backup للموقع الذي أنشأناه، كما سنتعلّم كيف نستعيد هذه النسخة.
  9. تُعتبر الصور كما نعلم من العناصر الأساسيّة في أيّ تطبيق، فهي تضفي لمسة جمالية بالإضافة إلى إعطائها انطباعًا توضيحيًّا لوظائف التطبيق الأساسيّة. سنتابع عملنا في هذه السلسلة من خلال شرح كيفيّة استخدام الصور في Xamarin.Froms. يتعامل Xamarin مع الصور النقطيّة bitmaps التي قد يختلف مفهومها قليلًا بالنسبة لأنظمة تشغيل Android وiOS وWindows، ولكن إذا اعتمدت على صيغ مثل JPEG وPNG وBMP فلن تعاني من أيّة مشاكل. يتعامل Xamarin.Forms مع الصور بأسلوبين رئيسيّين. يعتمد الأسلوب الأوّل على الصور المستقلة عن نوع الجهاز platform-independent أمّا الأسلوب الثاني فيعتمد على الصور المرتبطة بالجهاز platform-specific أو بمعنى أدق المرتبطة بنظام التشغيل الذي يعمل على الجهاز. ستناول الأسلوب الأوّل في درسنا هذا من خلال تطبيقين بسيطين يوضّحان ذلك. أمّا الأسلوب الثاني فسنؤجّل الحديث عنه لدرس آخر. يمكن عرض الصور ضمن تطبيقات Xamarin عن طريق عنصر مرئي يمثّله الصنف Image. وكما مع بقية العناصر المرئية التي ترث من الصنف VisualElement يمتلك هذا العنصر العديد من الخصائص المشتركة بينه وبين أي عنصر مرئي آخر كاللصيقة Label مثلًا. بالإضافة إلى خصائص أخرى تميّزه كالخاصيّة Source التي تُعبّر عن مصدر الصورة التي ستُعرض ضمنه. ستكون هذه الخاصيّة محور اهتمامنا في هذا الدرس. يمكن الحصول على الصور المستقلة عن الجهاز بأسلوبين مختلفين. ستحتاج في كلّ أسلوب منهما إلى إسناد كائن من الصنف ImageSource إلى الخاصيّة Source لكائن الصورة Image. يتمثّل الأسلوب الأوّل في الحصول على صور من الإنترنت مباشرةً، ويكون ذلك من خلال استدعاء التابع الساكن FromUri من الصنف ImageSource وتمرير عنوان Uri لملف الصورة على الإنترنت. أمّا الأسلوب الثاني فيتمثّل باستدعاء التابع الساكن FromResource من الصنف ImageSource أيضًا وتمرير اسم المصدر resource (الصورة) إليه. سيوضّح التطبيقين البسيطين التاليّين كيفيّة فعل ذلك. الحصول على صورة من الإنترنت أنشئ تطبيقًا جديدًا سمّه WebImageApp من النوعBlank App (Xamarin.Forms Portable) وأبق فقط على المشروعين WebImageApp (Portable) و WebImageApp.Droid كما اتفقنا من قبل. أضف صفحة محتوى جديدة ContentPage كما فعلنا في هذا الدرس. سمّها WebImagePage واحرص على أن تكون محتويات الملف WebImagePage.cs على الشكل التالي: using System; using Xamarin.Forms; namespace WebImageApp { public class WebImagePage : ContentPage { public WebImagePage() { Image webImage = new Image { VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand, Source = ImageSource.FromUri(new Uri("https://developer.xamarin.com/demo/IMG_2138.JPG?width=800")), Aspect = Aspect.AspectFit, BackgroundColor = Color.Accent }; Content = new StackLayout { Children = { webImage } }; } } } ثم انتقل إلى الملف App.cs واحرص على أن تكون بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new WebImagePage(); } شيفرة التطبيق بسيطة، فهي تعمل على إنشاء كائن صورة جديد وتُسنده إلى المتغيّر webImage، ثم تعمل على جعل كائن الصورة هذا ابنًا لمخطّط المكدّس كما هو واضح. ما يهمنا الآن هو خصائص كائن الصورة، فقد أسندنا القيمة LayoutOptions.FillAndExpand إلى كلّ من الخاصيّتين VerticalOptions وHorizontalOptions لكي تشغل الصورة كامل المساحة المتاحة لمخطّط المكدّس. أمّا بالنسبة للخاصيّة Source فهي تعبّر عن المصدر الذي سنحصل منه على الصورة. في تطبيقنا هذا سنحصل على الصورة من الإنترنت لذلك سنستخدم التابع الساكن ImageSource.FromUri الذي يُرجع كائنًا من الصف ImageSource. لاحظ أنّنا قد مرّرنا كائن جديد من النوع Uri إلى هذا التابع: ImageSource.FromUri(new Uri("https://developer.xamarin.com/demo/IMG_2138.JPG?width=800")) العنوان المستخدم هنا هو: "https://developer.xamarin.com/demo/IMG_2138.JPG?width=800" بالنسبة للخاصيّة Aspect فهي تُحدّد كيفيّة عرض الصورة ضمن عنصر الصورة Image. حيث أنّها تقبل قيمًا من المعدودة Aspect التي تحمل نفس الاسم. لهذه المعدودة ثلاثة عناصر: AspectFit وAspectFill وFill. استخدمنا في هذا التطبيق القيمة AspectFit وهي القيمة الافتراضيّة. وإليك ما تعنيه القيم الثلاث السابقة: AspectFit: تعني أنّ الصورة ستظهر ضمن العنصر بحيث تنسجم مع المساحة المخصّصة له، مع احترام النسبة الخاصّة بمظهر الصورة aspect ratio. AspectFill: في هذه الحالة ستظهر الصورة ضمن العنصر بحيث تحترم النسبة الخاصّة بمظهر الصورة aspect ratio، ولكنّها لن تهتم بالمساحة المخصّصة لهذا العنصر. لذلك فقد تظهر الصورة مقتطعةً إذا كانت أكبر من العنصر. Fill: تعني هذه القيمة أنّ الصورة ستظهر بحيث تشغل كامل المساحة المخصّص للعنصر بدون أيّ اعتبار النسبة الخاصّة بمظهر الصورة aspect ratio. لذلك فقط تظهر الصورة ممطوطة أفقيًّا أو رأسيًّا. لقد أسندت اللون Color.Accent إلى خاصيّة لون الخلفيّة BackgroundColor لعنصر الصورة لكي نرى المساحة الفعليّة التي يشغلها هذا العنصر. نفّذ البرنامج السابق باستخدام F5 لتحصل على شكل شبيه بما يلي، مع الانتباه إلى أنّني أجري التجارب على جهاز ذي شاشة قياسها 4.5 بوصة، لذلك قد تختلف النتائج التي تحصل عليها على جهازك في حال كان لديك شاشة ذات قياس أكبر. إذا كان الأمر كذلك فأنصح بحذف المقطع "?width=800" من عنوان Uri للصورة من الخاصيّة Source: لاحظ كيف ظهرت الصورة بحيث بقيت مساحة فارغة من الأعلى والأسفل. هذا بسبب أنّها احترمت النسبة الخاصّة بمظهر الصورة الأصليّة aspect ratio. إذا أجريت تعديلًا على الخاصيّة Aspect لتحمل القيمة Aspect.AspectFill فستحصل في هذه الحالة على شكل شبيه بما يلي: لاحظ هنا كيف ظهرت الصورة مقتطعة قليلًا. جرّب الآن الحالة الأخيرة، وهي إسناد القيمة Aspect.Fill إلى الخاصيّة Aspect لتحصل على شكل شبيه بما يلي: وكما هو متوّقع ظهرت الصورة بشكل ممطوط رأسيًّا لأنّها ستملأ كامل المساحة المتاحة لعنصر الصورة دون اعتبار للنسبة الخاصّة بمظهر الصورة الأصليّة كما أشرنا. الحصول على صورة من مصدر resource محلّي لا يختلف التعامل مع الصور في هذا الأسلوب مع أسلوب التعامل مع الصور التي نحصل عليها من الويب. باستثناء أنّنا نستخدم التابع الساكن FromResource من الصنف ImageSource. سنتناول في هذه الفقرة تطبيقًا عمليًّا لتصفّح مجموعة من الصور التي أعددتها خصيصًا لهذا الغرض. سيتم التصفّح عن طريق زرّين: "التالي" و"السابق" بالإضافة إلى وجود لصيقة تعرض معلومة بسيطة عن رقم الصورة الحالية التي يتم عرضها على عنصر الصورة. ستكون هذه الصور موجودة محلّيًّا ضمن التطبيق. لنبدأ الآن بإنشاء تطبيق جديد اسمه ResourceImageApp من النوعBlank App (Xamarin.Forms Portable) . أبق فقط على المشروعين ResourceImageApp (Portable) و ResourceImageApp.Droid. أضف صفحة محتوى جديدة ContentPage سمّها ResourceImagePage واحرص على أن تكون محتويات الملف ResourceImagePage.cs على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace ResourceImageApp 5 { 6 public class ResourceImagePage : ContentPage 7 { 8 private Button btnPrev; 9 private Button btnNext; 10 private Image resourceImage; 11 private Label lblInfo; 12 private int currentIndex = 1; 13 14 public ResourceImagePage() 15 { 16 lblInfo = new Label 17 { 18 VerticalOptions = LayoutOptions.Start, 19 HorizontalOptions = LayoutOptions.FillAndExpand, 20 HorizontalTextAlignment = TextAlignment.Center, 21 TextColor = Color.Accent, 22 FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) 23 }; 24 25 resourceImage = new Image 26 { 27 VerticalOptions = LayoutOptions.FillAndExpand, 28 HorizontalOptions = LayoutOptions.FillAndExpand, 29 Aspect = Aspect.AspectFit 30 }; 31 32 btnPrev = new Button 33 { 34 Text = "السابق", 35 HorizontalOptions = LayoutOptions.FillAndExpand, 36 IsEnabled = false 37 }; 38 btnPrev.Clicked += BtnPrev_Clicked; 39 40 btnNext = new Button 41 { 42 Text = "التالي", 43 HorizontalOptions = LayoutOptions.FillAndExpand 44 }; 45 btnNext.Clicked += BtnNext_Clicked; 46 47 StackLayout buttonsLayout = new StackLayout 48 { 49 Orientation = StackOrientation.Horizontal, 50 VerticalOptions = LayoutOptions.End, 51 HorizontalOptions = LayoutOptions.FillAndExpand, 52 Children = 53 { 54 btnNext, 55 btnPrev 56 } 57 }; 58 59 Content = new StackLayout 60 { 61 Children = { 62 lblInfo, 63 resourceImage, 64 buttonsLayout 65 } 66 }; 67 68 Padding = new Thickness(5, 5, 5, 5); 69 70 UpdateScreen(); 71 } 72 73 private void BtnPrev_Clicked(object sender, EventArgs e) 74 { 75 currentIndex--; 76 if (currentIndex == 1) 77 { 78 btnPrev.IsEnabled = false; 79 } 80 81 btnNext.IsEnabled = true; 82 83 UpdateScreen(); 84 } 85 86 private void BtnNext_Clicked(object sender, EventArgs e) 87 { 88 currentIndex++; 89 if (currentIndex == 5) 90 { 91 btnNext.IsEnabled = false; 92 } 93 94 btnPrev.IsEnabled = true; 95 96 UpdateScreen(); 97 } 98 99 private void UpdateScreen() 100 { 101 lblInfo.Text = string.Format("الصورة {0} من 5", currentIndex); 102 string path = string.Format("ResourceImageApp.Images.res0{0}.jpg", currentIndex); 103 resourceImage.Source = ImageSource.FromResource(path); 104 } 105 } 106 } ثم انتقل إلى الملف App.cs واحرص على أن تكون بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new ResourceImagePage(); } انقر بزر الفأرة الأيمن على المشروع المشترك ResourceImageApp (Portable) ثم اختر Add ومن القائمة الجانبيّة التي ستظهر اختر New Folder. سينشئ ذلك مجلّدًا جديدًا ضمن هذا المشروع، سمّه Images. ثم انقر بعد ذلك بزر الفأرة الأيمن على مجلّدنا الجديد Images واختر Add وبعدها Existing Item سيظهر مربّع حوار يسمح لك باختيار الصور المراد إضافتها كمصادر resources إلى هذا المجلّد. اختر ملفّات الصور: res01.jpg وres02.jpg وres03.jpg و res04.jpg وres05.jpg (انقر هنا لتحميلهاImages.zip). تحتوي هذه الصور بالترتيب على العبارة التالية "مرحبًا بكم في أكاديميّة حسّوب". انقر بزر الفأرة الأيمن مجدّدًا على كل اسم ملف أضفته قبل قليل إلى المشروع واختر من القائمة الأمر Properties. ستظهر خصائص الملف، اضبط الخاصيّة Build Action لتصبح Embedded Resource كما هو واضح من الشكل التالي لأحد الملفات: نفّذ البرنامج باستخدام F5 لتحصل على شكل شبيه بما يلي: لنلاحظ في البداية أنّ التطبيق يعرض الصورة الأولى التي يمثّلها الملف res01.jpg. كما نلاحظ أنّ زر "السابق" غير مفعّل أي أنّنا لا نستطيع الرجوع إلى الوراء، وأيضًا العبارة التوضيحيّة في الأعلى التي تعرض رقم الصورة الحاليّة. إذا جرّبت نقر زر "التالي" ستنتقل إلى الصورة رقم 2 (الملف res02.jpg) وسيصبح الزر "السابق" مفعّلًا ليسمح لنا بالعودة إلى الصورة رقم 1. إذا تابعت النقر على زر "التالي" ستستعرض تباعًا الملفات بالترتيب، حتى إذا وصلت إلى الملف رقم 5 سيصبح الزر "التالي" غير مفعّل لأنّه لا توجد أيّة صور بعد الملف رقم 5. انظر إلى الشكل التالي: بمجرّد أن تنقر الزر "السابق" سيعود زر "التالي" مفعّلًا من جديد. لنناقش الشيفرة البرمجيّة المشغّلة لهذا التطبيق. نلاحظ في الأسطر من 8 حتى 11 أنّني قد صرّحت عن حقول خاصّة تمثّل معظم العناصر المرئيّة التي سنتعامل معها في هذا التطبيق وهي عنصر صورة وعنصر لصيقة وزرّين للتالي والسابق. كما صرّحت في السطر 12 عن متغيّر خاص currentIndex يمثّل رقم الملف الحالي الذي نرغب بعرضه ضمن ملف الصورة وأسندت له القيمة التمهيديّة 1. بالنسبة لبانية الصنف ResourceImagePage فهي تعمل على تهيئة جميع العناصر المرئيّة المستخدمة كما نعلم. حيث نضبط في الأسطر من 16 حتى 23 خصائص اللصيقة lblInfo حيث أسندنا القيمة LayoutOptions.Start للخاصيّة VerticalOptions لكي تظهر أوّل (أعلى) الشاشة. كما أسندنا القيمة TextAlignment.Center لخاصيّة محاذاة النص الأفقيّة HorizontalTextAlignment لكي يظهر النص موسّطًا بشكل أفقي. أمّا بالنسبة لعنصر الصورة resourceImage فقمنا بضبط خصائصه في الأسطر من 25 حتى 30 حيث جعلنا قيمة الخاصيّة Aspect له تساوي Aspect.AspectFit لكي تملأ الصورة الحيّز المتاح لها مع احترام النسبة الخاصّة بمظهر الصورة. تأتي بعد ذلك عمليّة ضبط الزرّين btnPrev (السابق) و btnNext (التالي) من الأسطر 32 حتى 45. سنضيف هذين الزرّين لاحقًا إلى مخطّط مكدّس خاص بهما هو buttonsLayout والسبب في ذلك أنّنا نريد أن نجعل هذين الزرّين متجاورين أفقيًّا مع تعبئة المساحة الأفقيّة المتاحة لهما (لاحظ القيمة LayoutOptions.FillAndExpand للخاصيّة HorizontalOptions لكل منهما). نصرّح عن مخطّط المكدّس buttonsLayout في الأسطر من 47 حتى 57. سنضيف لهذا المخطّط الزرّين btnPrev و btnNext كما سنضبط خاصيّة الاتجاه Orientation له لتكون أفقيّة StackOrientation.Horizontal لكي يُظهِر الزرّين السابقين بشكل أفقيّ وليس رأسيّ. سنضيف مخطّط المكدّس هذا بعد قليل إلى مخطّط المكدّس الأساسيّ للصفحة. نضبط الخاصيّة Content للصفحة في الأسطر 59 حتى 66 بحيث ننشئ مخطّط مكدّس جديد يحتوي على العناصر: اللصيقة والصورة ومخطّط المكدّس الخاص بالأزرار على الترتيب. وهذا هو سبب ظهور العناصر السابقة بهذا الترتيب. انظر إلى المخطّط التمثيلي التالي الذي يوضّح التموضع التقريبي للعناصر السابقة على الصفحة: المستطيل البني في الأسفل يمثّل مخطّط المكدّس buttonsLayout بالنسبة لمعالجيّ حدثيّ النقر بالنسبة للزرّين btnPrev (الأسطر من 73 إلى 84) وbtnNext (الأسطر من 86 إلى 97) فيحتويان على منطق برمجيّ بسيط يضمن التنقّل الصحيح بين الصور الخمس الموجودة ضمن مصدر resource التطبيق. وأخيرًا يعمل التابع UpdateScreen (الأسطر من 99 إلى 104) على تحديث البيانات التي تظهر على عنصري اللصيقة والصورة عند كلّ نقر لزر "التالي" و"السابق". يتطلّب التابع FromResource من الصنف ImageSource وسيطًا نصيًّا يمثّل معرّف الملف. يتكوّن معرّف الملف من اسم التطبيق (أو بمعنى أدق التجميعة Assembly) متبوعًا بنقطة ثم اسم المجلّد الذي يحتوي على ملف الصورة متبوعًا بنقطة أخرى، ثم أخيرًا اسم الملف مع امتداده. في مثالنا هذا يكون معرّف الملف res01.jpg مثلًا على الشكل التالي: ResourceImageApp.Images.res01.jpg نُشكّل هذا المعرّف في السطر 102 من خلال التابع الساكن Format من الصنف string. حيث نختار الملف المطلوب من خلال قيمة المتغيّر currentIndex الذي يمثّل رقم ملف الصورة الحالي. ربما تبدو شيفرة هذا التطبيق كبيرة نسبيًّا، ولكن أؤكّد لك أنّك ستكتب أضعافًا منها في أيّ برنامج تجاري من الممكن أن تعمل عليه! يعود سبب ذلك إلى الأسلوب الذي انتهجناه منذ بداية هذه السلسلة حتى الآن، وهو الاعتماد على الشيفرة البرمجيّة في إنشاء واجهة المستخدم بالكامل. ورغم أنّه من الممكن اتباع أساليب جيّدة تساهم في ترتيب وتنظيم هذه الشيفرة بشكل كبير، إلّا أنّ العديد من المطوّرين يفضّلون استخدام الأسلوب الآخر في تطوير تطبيقات باستخدام Xamarin.Forms والذي يتمثّل في استخدام XAML (رُماز شبيه بـ XML) والذي يسمح بفصل واجهة المستخدم عن الشيفرة البرمجيّة. سنتناول هذا الموضوع بالطبع في دروس قادمة. الخلاصة تعاملنا في هذا الدرس مع الصور في Xamarin.Forms. حيث ناقشنا الموضوع من جانبين مختلفين. يهتم الجانب الأوّل بتحميل الصورة من الإنترنت (ومن الممكن من شبكة محليّة أيضًا)، أمّا الجانب الثاني فيهتمّ بتحميل الصورة من مصدر محلّي. هناك المزيد للحديث عنه في هذا الموضوع، وخاصّةً فيما يتعلّق بالحصول على الصور من خلال مصادر الصور المرتبطة بالجهاز platform-specific أو بمعنى أدق المرتبطة بنظام التشغيل الذي يعمل على الجهاز والذي هو نظام أندرويد في حالتنا. أي ما يشبه الأسلوب المتبع عند العمل مع مصادر الصور من خلال Android Studio. سنغطّي هذا الموضوع لاحقًا في هذه السلسلة.
  10. مبدأ فصل الواجهات Interface Segregation Principle أو اختصارًا ISP هو أحد المبادئ الشهيرة من مبادئ SOLID في التصميم الكائنيّ. أوّل من قدّم هذا المبدأ روبرت مارتن في سلسلة مقالات في عام 1996، ويهدف هذا المبدأ إلى تجنُّب إنشاء واجهات "سمينة". الواجهة interface هي عبارة عن تجريد abstraction لناحية وظيفيّة (أو أكثر) ترث منها أنواع (أصناف) لدعم هذه الناحيّة الوظيفيّة، ونقول في هذه الحالة أنّ الصنف الابن يُحقّق الواجهة. فمثلًا تُعرّف الواجهة IComparer في لغة C# إمكانيّة المقارنة بين كائنين يحقّق صنفهما الواجهة IComparer. في لغات برمجة أخرى، قد لا يوجد تمثيل منفصل للواجهة، وإنّما يتم إنشاء أصناف ذات طبيعة مجرّدة لهذا الغرض، نسميها أصناف واجهة، وهي التي سنتحدّث عنها في هذا المقال. تظهر الواجهات السمينة (أو الملوّثة) بسبب توسعة صنف واجهة حالي ببعض النواحي الوظيفيّة الجديدة المفيدة لمجموعة جزئيّة فقط من التوابع methods. تؤدّي هذه الظاهرة في نهاية الأمر إلى إنشاء توابع ليس لها أيّ فائدة سوى تحقيق الواجهة للتمكّن من استخدامها. وهذا أمر سيّء بالطبع. تُعتبر تلك التوابع خطرة وهي تخرق على أيّة حال مبدأ LSP أيضًا. ينص مبدأ ISP كما كتبه المؤلّف: ينبغي أن يكون لكلّ واجهة هدف معرّف بوضوح ويُعبّر عن ناحية وظيفيّة مُحدّدة للمسألة المطروحة. الحل الأمثل (برأيي) لتحقيق هذا المبدأ هو استخدام الوراثة المتعدّدة عند تحقيق الواجهات. سيعمل هذا الأسلوب على فصل النواحي الوظيفيّة التي لا ترتبط منطقيًّا مع بعضها ويلغي الاعتماديّات dependencies الخاطئة. لنستعرض مثالًا يخرق مبدأ ISP. يتناول هذا المثال واجهة خاصّة بالنواحي الوظيفيّة الّتي من الممكن أن تمتلكها أي سيّارة: /* Bad example */ class CarOperation { public: virtual void steer(int degrees) = 0; virtual void pullHandbrake() = 0; virtual void accelerate() = 0; virtual void shift(int gear) = 0; virtual void toggleAirConditioning() = 0; }; هناك العديد من المزايا المألوفة التي توفّرها السيّارات، فكل سيّارة لها عجلة قيادة ومدوسة وقود بالإضافة إلى ناقل حركة يدوي وفرامل اليد. ولكن ماذا عن السيّارات التي لها ناقل حركة أوتوماتيكي؟ ففي مثل هذه السيّارات لا يوجد ناقل حركة يدوي، فالصنف الواجهة الوارد في الشيفرة السابقة سيجبر أي صنف ابن يرث منه على استخدام الطريقة shift التي تُعبّر عن ناقل حركة اليدوي حتى ولو كان هذا الصنف الابن يمثّل سيّارة ذات ناقل أوتوماتيكي. وينطبق نفس الكلام على الطريقة toggleAirConditioning التي تعبّر عن تشغيل جهاز التكييف في السيّارة رغم أنّ بعض السيّارات لا تملك جهاز تكييف. الأفضل في هذه الحالة أن نجزّئ الصنف الواجهة CarOperation إلى عدد من أصناف الواجهات الأصغر بحيث تُعبّر كلّ منها عن مجموعة محدّدة مترابطة منطقيًّا من المزايا: class BasicCarOperation { public: virtual void steer(int degrees) = 0; virtual void pullHandbrake() = 0; virtual void accelerate() = 0; }; class GearboxCarOperation { public: virtual void shift(int gear) = 0; }; class AirConditioningCarOperation { public: virtual void toggleAirConditioning() = 0; }; class AlfaRomeo166 : public BasicCarOperation, GearboxCarOperation, AirConditioningCarOperation { /* Implementation of all the interfaces. */ }; class SkodaFavorit136L : public BasicCarOperation, GearboxCarOperation { /* No air conditioning for old cars. */ }; سيلجأ الصنفان الابنان AlfaRomeo166 وSkodaFavorit136L إلى الوراثة المتعدّدة من الأصناف الواجهات. فإذا أردنا تشغيل جهاز التكييف في سيّارة ما عن طريق الدالّة beCool فسيكون ذلك كالتالي: void beCool(AirConditioningCarOperation* vehicle) { vehicle->toggleAirConditioning(); } نلاحظ أنّنا الدالّة beCool تقبل وسيطًا واحدًا وهو مؤشّر إلى كائن من النوع AirConditioningCarOperation، وهكذا فإنّ أي صنف ابن يرث من الصنف الواجهة AirConditioningCarOperation يمكن أن يُمرَّر إلى هذه الدالة بصرف النظر عن أيّ أصناف واجهات أخرى يرث منها هذا الصنف الابن. وهنا يكمن جمال مبدأ فصل الواجهات. سنحصل تمامًا على ما نريد، ليس أكثر وليس أقل، مما يجعل من الشيفرة البرمجيّة أسهل للصيانة ولإعادة الاستخدام وللفهم، ويساعد ذلك على تجنّب كم كبير من الأخطاء عندما نريد تعديل الشيفرة في المستقبل. المصادر: http://www.oodesign.com/interface-segregation-principle.html http://en.wikipedia.org/wiki/Interface_segregation_principle http://www.objectmentor.com/resources/articles/isp.pdf ترجمة -وبتصرّف- للمقال Interface Segregation Principle لصاحبه Radek Pazdera.
  11. أهلًا بك أخي. أعتذر عن التأخر في الرد. لا أدري لماذا لم ينبهني الموقع على تعليقك. على العموم يمكنك مراجعة الرابط التالي الذي يوضّح بالتفصيل كيفية ذلك: https://developer.xamarin.com/guides/android/getting_started/installation/accelerating_android_emulators/
  12. انتهينا في الدرس السابق من عمليّة تنصيب وتكوين شير بوينت على خادوم التطبيق والويب. سنتابع العمل الذي بدأناه في الدرس السابق وذلك من خلال إعداد الخدمات الأساسيّة التي نرغب بتضمينها في شير بوينت. كما سننشئ موقع ويب لشركتنا الافتراضيّة. لكنّنا سنبدأ في البداية بإنشاء حساب مستخدم خاص بإعداد خدمات شير بوينت. إنشاء حساب مستخدم خاص بإعداد خدمات شير بوينت سنحتاج من أجل هذا الدرس إلى إنشاء حساب مخصّص لإعداد خدمات شير بوينت. سنسمّ هذا الحساب SPService. وعلى الرغم من أنّه كان من الممكن أن نستخدم الحساب SPAdmin أو حتى SPFarm إلّا أنّ مايكروسوفت تنصح بتخصيص مستخدم منفصل لهذه العمليّة. سننتقل إلى خادوم المتحكم بالمجال HSB-DC وسننشئ حساب اسمه SPService وذلك باستخدام طريقة النسخ من حساب موجود مسبقًا، أي كما فعلنا تمامًا في فقرة إنشاء حساب مدير المجال hsoub-sp.com في هذا الدرس. إعداد خدمات شير بوينت توقفنا في الدرس السابق عند نافذة الترحيب الرئيسيّة بعد الانتهاء من تنصيب وتكوين شير بوينت. انظر إلى الشكل التالي: انقر الزر Start the Wizard لتظهر لك الصفحة التالية المسؤولة عن تحديد الخدمات التي نرغب بتشغيلها مع شير بوينت. سأترك الخدمات الافتراضية كما هي دون أي تعديل. علمًا أنّه من الممكن تعديل هذه الخدمات فيما بعد. انظر الشكل التالي: لقد استخدمت اسم المستخدم HSOUB-SP\SPService مع كلمة المرور الخاصة به كما يظهر من الشكل السابق لإعداد هذه الخدمات. لاحظ كيف تبدو الخدمات في الأسفل وبجوار كل منها صندوق اختيار. توجد العديد من الخدمات الأخرى ولكنها لا تظهر في الصورة. انقر زر Next الموجود في الأعلى. سيأخذ الأمر بعضًا من الوقت حتى يتم تجهيز الخدمات المختارة. إنشاء موقع ويب بعد الانتهاء من تجهيز الخدمات ستصل إلى صفحة تسمح لك بإنشاء موقع ويب خاص بالشركة. انظر إلى الشكل التالي: أدخل Hsoub Inc. للحقل Title كما يظهر من الشكل السابق. يمكنك بالطبع اختيار أي اسم ترغبه. احرص على أن تختار الرمز "/" من القائمة المنسدلة الخاصة بعنوان URL، واحرص أيضًا أن تكون النسخة هي 2013 من القائمة المنسدلة Select experience version كما تأكّد أنّك قد اخترت Team Site كنوع قالب Template لهذا الموقع كما يظهر من الشكل السابق. انقر بعد ذلك الزر OK من الأعلى سيستغرق ذلك بعض الوقت كي يعمل شير بوينت على إنشاء الموقع المطلوب. بعد ذلك ستحصل على صفحة تُظهر تقريرًا بالعمل التي تمّ إنجازه. ستلاحظ وجود اسم الموقع Hsoub Inc بالإضافة إلى الخدمات التي تمّ تفعيها مع شير بوينت. تشير هذه الصفحة إلى أنّ الأمور تجري على ما يرام. انقر الآن الزر Finish في الأسفل للانتهاء من هذه العمليّة وللانتقال إلى صفحة الإدارة الرئيسيّة. ملاحظة كان من الممكن أن نفصل بين إعداد خدمات شير بوينت، وبين إنشاء موقع جديد. ولكن أجرينا كلًا من هذين الأمرين معًا طلبًا للاختصار. تجهيز حاسوب مكتبي وضمّه إلى المجال hsoub-sp.com لكي نرى نتيجة عملنا السابق، علينا أن نستخدم حاسوب مكتبي عادي للدخول إلى الموقع الذي أنشأناه قبل قليل. من الممكن أن تستخدم نظام تشغيل مثل Windows 8.1 أو Windows 10 وعلى كلّ الأحوال لن تختلف طريقة تجهيزه. نحتاج في البداية إلى تغيير عنوان IP له. راجع الفقرة "ضبط عنوان IP للخادوم" من هذا الدرس لتعرف كيف نفعل ذلك مع الانتباه إلى أنّ عنوان الـ IP للحاسوب المكتبي سيكون 192.168.25.11 أي لا تستخدم نفس العنوان الموجود في ذلك الدرس. بعد ضبط عنوان IP للحاسوب المكتبي يصبح أمامنا خياران. الأوّل هو ضم هذا الحاسوب المكتبي إلى المجال hsoub-sp.com كما فعلنا مرارًا في هذه السلسلة (راجع مثلًا فقرة "ضمّ الخادوم إلى المجال hsoub-sp.com" في هذا الدرس). أمّا الخيار الثاني فيتمثّل في عدم ضم الحاسوب المكتبي إلى النطاق hsoub-sp.com والوصول مباشرةً إلى الموقع المطلوب من خلال المتصفّح. ملاحظة يجب التأكّد دومًا من أنّ عنوان DNS Server الخاص بخادوم المتحكّم بالمجال HSB-DC (عنوانه 192.168.25.10) هو العنوان الذي يأتي في الترتيب الأوّل من بين عناوين خواديم DNS الخاصّة بالحاسوب المكتبي وذلك في حال وجود أكثر من عنوان، وإلّا ستقع في متاعب كبيرة! قد تحدث مثل هذه الحالة عندما يكون الحاسوب المكتبي متصلًا بالإنترنت أيضًا فيمتلك في هذه الحالة عناوين خواديم DNS إضافية مثل 8.8.8.8. بصرف النظر عن الخيار الذي استخدمته، افتح متصفّح الويب Internet Explorer (يمكنك بالطبع استخدام أي متصفّح تريده ولكن شير بوينت متوافق مع هذا المتصفّح بشكل أكبر). ثم اكتب في شريط العنوان http://hsb-sp واضغط المفتاح Enter سيأخذ ذلك القليل من الوقت حتى يعرض لك المتصفّح رسالة تطلب منك إدخال اسم المستخدم وكلمة المرور. انظر الشكل التالي: لاحظ أنّني أسجّل الدخول باسم المستخدم SPAdmin (مع كتابة اسم المجال أولًا). انقر زر OK للوصول إلى موقع الويب. بعد أن ينجح تسجيل الدخول سنصل إلى الصفحة الرئيسيّة للموقع كما في الشكل التالي: هذه هي الصفحة الرئيسيّة للموقع الذي أنشأناه. لاحظ كيف يظهر عنوان الموقع ضمن شريط عنوان الصفحة. وأيضًا كيف يظهر اسم المستخدم SPAdmin في الزاوية اليمنى العليا. الموقع الحالي فارغ تمامًا. انقر محتويات الموقع Site Contents التي تظهر في العمود الأيمن (البند الرابع) من الشكل السابق لتحصل على الشكل التالي: يمكننا من هذا المكان إضافة جميع تطبيقات شير بيونت مثل مكتبات الوثائق والقوائم وجهات الاتصال ومكتبات الصور وغيرها. سنعمل في الدرس القادم على إضافة بعض هذه التطبيقات إلى موقعنا. كما سننشئ مستخدم جديد سيكون بمثابة موظّف ذو صلاحيّات محدّدة ضمن الموقع. الخلاصة لقد اختتمنا في هذا الدرس مشوارنا في بناء وتجهيز مزرعة شير بوينت متكاملة وقابلة للتوسّع. حيث أنشأنا مستخدمًا مستقلًا لإعداد خدمات شير بوينت (كما تنصح مايكروسوفت)، بالإضافة إلى إنشائنا موقع ويب تجريبي، وتجهيزنا لحاسوب مكتبي بهدف الوصول لهذا الموقع والاطلاع على محتوياته. سجّلنا الدخول إلى الموقع باستخدام الحساب SPAdmin وهو الحساب الذي سنعمل من خلاله على إدارة هذا الموقع، وإدارة تطبيقاته المختلفة، والتحكّم بصلاحيّات المستخدمين ضمنه بالشكل الذي يخدم الشركة التي يمثّلها.
  13. وصلنا في هذا الدرس من سلسلة تنصيب شير بوينت إلى المرحلة النهائيّة التي ستتوّج عملنا في إعداد مزرعة خواديم شير بوينت وتجهيزها للاستثمار، وذلك بتنصيب شير بيونت نفسه. لقد تعاملنا في الدرس السابق مع المتطلّبات الأوليّة لشير بوينت والتي يحتاجها لكي نستطيع تنصيبه على الخادوم المخصّص له. سنتمكّن بعد إتمام هذا الدرس من تشغيل شير بوينت ووضعه بالاستثمار عن طريق البدء بإنشاء مواقع ويب تعمل عليه. إنشاء حساب مستخدم خاص للمزرعة سنحتاج من أجل هذا الدرس إلى إنشاء حساب مخصّص لإدارة مزرعة الخواديم التي سننشئها في الفقرة التالية. سننتقل إلى خادوم المتحكم بالمجال HSB-DC وسننشئ حساب اسمه SPFarm وذلك باستخدام طريقة النسخ من حساب موجود مسبقًا، أي كما فعلنا تمامًا في فقرة إنشاء حساب مدير المجال hsoub-sp.com في هذا الدرس. تغيير إعدادات الجدار الناري لخادوم قاعدة البيانات لا يسمح الجدار الناري Firewall الموجود في خادوم قاعدة البيانات HSB-DB بالوصول إلى SQL Server بشكل افتراضي. لذلك فمن الواجب أولًا إضافة قاعدة جديدة rule تسمح بالاتصال بـ SQL Server بشكل مشابه لما فعلناه مسبقًا. انتقل إلى خادوم قاعدة البيانات HSB-DB، واتبع الخطوات الموجودة في هذا الدرس في فقرة "التحقّق من عنوان IP قبل استخدامه" لكي تحصل على نافذة إعدادات الجدار الناري المتقدّمة. من الطرف الأيسر للنافذة انقر بزر الفأرة الأيسر على Inbound Rules ثم اختر New Rule كما في الشكل التالي: ستظهر لك نافذة تسمح لك بتحديد نوع القاعدة المطلوبة. اختر Port ثم انقر Next. بعد ذلك سيُطلَب منك تحديد نوع الاتصال والمنفذ. سنحدّد نوع الاتصال ليكون TCP والمنفذ 1433 كما في الشكل التالي: انقر الزر Next لتحصل على نافذة تخيّرك بطريقة التعامل مع الاتصال حين وروده. احرص على اختيار Allow the connection ثم انقر Next. ستحصل على النافذة التالية: احرص على أن تكون الصناديق في حالة اختيار كما في الشكل السابق، ثم انقر Next. أخيرًا ستظهر النافذة التي تسمح لك بكتابة اسم القاعدة. اكتب الاسم AllowSqlServerAccess ثم انقر زر Finish. تنصيب شير بوينت انتقل إلى الخادوم HSB-SP ثم شغّل برنامج الإعداد الخاص بـ SharePoint 2013 Server من جديد، والذي كنّا قد نزّلناه من موقع مايكروسوفت في الدرس السابق. ستحصل على النافذة التالية: انقر Install SharePoint Server كما هو موضّح في الشكل السابق لتبدأ عمليّة الإعداد. سيطلب منك ويندوز السماح لبرنامج الإعداد بالعمل، اقبل ذلك بأن تنقر زر Yes. سيعرض برنامج الاعداد نافذة جديدة تطلب من المستخدم إدخال الرقم التسلسلي الخاص بالنسخة. سنستخدم الرقم التسلسلي: NQTMW-K63MQ-39G6H-B2CH9-FRDWJ والذي يعطينا فترة تجريبيّة لمدة 180 يوم. انظر إلى الشكل التالي: انقر بعد ذلك على الزر Continue لمتابعة عمليّة الإعداد. ستحصل بعد ذلك على نافذة الترخيص الخاصّة بهذا المنتج، وافق على هذه الاتفاقيّة بالنقر على صندوق الاختيار الموجود في الأسفل ثم انقر زر Continue لتحصل على النافذة التي تحدّد النسخة التي ترغب بتنصيبها على هذا الخادوم. انظر الشكل التالي: تخيّرنا هذه النافذة بين تنصيب شير بوينت بشكل كامل Complete (الخيار الأوّل) وبين تنصيبه لأغراض تجريبيّة وتطويريّة (الخيار الثاني). سنختار الخيار الأوّل بالتأكيد، وبالنهاية ننقر الزر Install Now. ستبدأ عند ذلك عمليّة التنصيب التي قد تستغرق بعض الوقت بحسب إمكانيّات الخادوم الذي تستخدمه حاليًّا. عندما ينتهي برنامج الإعداد من تنصيب شير بوينت سيخبرنا برنامج الإعداد بهذا الأمر ويعرض علينا تشغيل برنامج التكوين الخاص بشير بوينت الذي يسمح بتجهيزه ووضعه بالاستثمار. سنؤجّل ذلك قليلًا لكي يتسنّى لنا التأكّد من وجود تحديثات جديدة لنظام تشغيل ويندوز كما وأن فعلنا ذلك في هذا الدرس. لذلك فعندما يعرض برنامج الإعداد هذه النافذة احرص على إزالة الاختيار من الصندوق في الأسفل كما في الشكل التالي، ثم انقر على زر الإغلاق Close لتنتهي عمليّة الإعداد. بعد أن تنتهي من التحقّق من وجود تحديثات جديدة لويندوز، انقر زر ابدأ Start لتظهر صفحة البداية. ثم اكتب مباشرةً العبارة SharePoint 2013 Products Configuration Wizard ليعمل الويندوز على البحث عن هذه الجملة أثناء كتابتك لها. ستظهر في القائمة نتيجة واحدة تُعبّر عن برنامج تكوين شير بوينت، انقر على هذه النتيجة ليبدأ عمل هذا البرنامج. سيطلب منك ويندوز أولًا السماح لهذا البرنامج بالعمل، وافق على ذلك بالنقر على Yes. ستظهر نافذة الترحيب الرئيسيّة التي تخبرنا بالأمور التي يحتاجها برنامج التكوين لكي يضبط شير بوينت بالطريقة الصحيحة. انظر الشكل التالي: انقر الزر Next. من الممكن أن تحصل على رسالة تفيد أنّ برنامج التكوين يحتاج لأن يُوقف بعض الخدمات على الخادوم. انقر الزر Yes للموافقة على ذلك. سيعرض برنامج التكوين بعد ذلك نافذة تُخيّرك بين الاتصال بمزرعة خواديم موجودة مسبقًا، أم أنّك تريد إنشاء مزرعة جديدة. سنختار إنشاء مزرعة جديدة بالطبع. اختر الخيار Create a new server farm كما هو موضّح بالشكل التالي ثم انقر Next: انقر الزر Next للانتقال إلى النافذة الخاصة بتحديد الخادوم الذي يعمل عليه SQL Server ولتحديد اسم قاعدة البيانات واسم المستخدم الذي سيقوم بهذه العمليّة. احرص على أن تكون الإعدادات مشابهة لما يظهر في الشكل التالي: لاحظ أنّنا استخدمنا SPFarm كاسم مستخدم. كان من الممكن أن نستخدم SPAdmin بدلًا عنه، ولكن تنصح مايكروسوفت بفصل مهام المستخدمين عن بعضهم البعض. انقر الزر Next ليطلب منك برنامج التكوين إدخال كلمة مرور Passphrase خاصة بالمزرعة. اختر كلمة مناسبة ثم انقر Next. ستظهر نافذة تسمح لك بتعيين منفذ port خاص بمركز إدارة شير بوينت الذي سنتعرّف عليه لاحقًا. سأختار المنفذ 44444 له كما في الشكل التالي: انقر الزر Next لتحصل على ملخص نهائي قبل تطبيق الاعدادات. انظر الشكل التالي: تأمّل هذه الإعدادات قليلًا، ثم انقر الزر Next ليعمل برنامج التكوين على ضبط هذه الإعدادات وتجهيز شير بوينت للعمل. ستأخذ هذه العملية بعض الوقت. بعد الانتهاء انقر الزر Finish من الأسفل، سيؤدي ذلك إلى تشغيل متصفّح الإنترنت Internet Explorer وتوجيهه إلى الصفحة الخاصة بإدارة شير بوينت، وهي عبارة عن تطبيق ويب يسمح بإدارة مكوّنات شير بوينت. ستطلب منك هذه الصفحة بيانات الدخول، سنسجّل الدخول باسم المستخدم SPAdmin كما في الشكل التالي: بعد ذلك انقر زر OK. بعد تسجيل الدخول سنصل إلى الصفحة الترحيبيّة التي تشير إلى نجاح عمليّة التكوين بشكل صحيح. تهانينا! يظهر من هذه الصفحة أنّ شير بوينت يخيّرنا بين تشغيل برنامج إعداد جديد لتهيئة مزرعتنا الجديدة لإنشاء موقع ويب جديد، أو أن نفعل ذلك بأنفسنا. سنختار تشغيل برنامج الإعداد Start the Wizard، وسنكمل هذا الموضوع في الدرس القادم. الخلاصة لقد أنجزنا في هذا الدرس المهم العديد من الخطوات. فقد أنشأنا في البداية حساب جديد اسمه SPFarm ضمن خادوم المتحكم بالمجال HSB-DC لإدارة عمليّة تنصيب شير بوينت، ثمّ أضفنا قاعدة جديدة إلى الجدار الناري للخادوم HSB-DB لكي نسمح للتطبيقات بالوصول إلى SQL Server. وفي النهاية عملنا على تنصيب شير بوينت وتكوينه لوضعه بالاستثمار بشكل مناسب. لم يتبق الكثير لفعله لكي نبدأ باستثمار شير بوينت. سنعمل في الدرس القادم على إنشاء موقع ويب جديد وتجهيز حاسوب خاص للسماح للمستخدمين بالدخول إلى شير بوينت واستثماره.
  14. أهلًا وسهلًا. هل من الممكن أن أعرف ما هي رسالة الخطأ التي ظهرت لك. وإذا كان من الممكن أن تأخذي لقطة للشاشة يكون أفضل.
  15. تعاملنا في الدروس السابقة في هذه السلسلة مع عناصر مرئيّة وظيفتها الأساسيّة عرض البيانات بشكل مناسب إلى المستخدم. في الحقيقة يوجد نوع آخر من العناصر المرئيّة، يتضمّن تلك العناصر التي يمكن أن يتفاعل المستخدم معها لتنفيذ أمر أو مهمّة معيّنة. سنتناول في هذا الدرس الممتع عنصر الزر Button وهو عنصر مرئي يسمح للمستخدم عند نقره (أو بالأحرى لمسه) بأن ينفّذ أمرًا معيّنًا. تطبيق العدّاد وهو تطبيق بسيط، الغرض منه فهم كيفيّة التعامل مع الأزرار في Xamarin.Forms. تتكوّن واجهة هذا التطبيق من زرّين بالإضافة إلى لصيقة. فكرة التطبيق بسيطة، فعندما ينقر المستخدم أحد الزرّين يزداد العدد المعروض على اللصيقة بمقدار واحد، وعندما ينقر على الزر الآخر ينقص هذا العدد بمقدار واحد أيضًا. أنشئ تطبيقًا جديدًا من النوع Blank App (Xamarin.Forms Portable) كما هو معتاد وسمّه CounterApp، وأبق فقط على المشروعين CounterApp.Droid و CounterApp (Portable) ضمنه. أضف صفحة محتوى ContentPage جديدة سمّها CounterPage. احرص على أن يكون محتوى الملف CounterPage.cs على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace CounterApp 5 { 6 public class CounterPage : ContentPage 7 { 8 private int counter = 0; 9 private Label lblDisplay; 10 11 public CounterPage() 12 { 13 Button btnIncrement = new Button 14 { 15 Text = "+", 16 HorizontalOptions = LayoutOptions.CenterAndExpand, 17 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 18 }; 19 btnIncrement.Clicked += btnIncrement_Clicked; 20 21 Button btnDecrement = new Button 22 { 23 Text = "-", 24 HorizontalOptions = LayoutOptions.CenterAndExpand, 25 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 26 }; 27 btnDecrement.Clicked += BtnDecrement_Clicked; 28 29 lblDisplay = new Label 30 { 31 Text = "0", 32 TextColor = Color.Accent, 33 HorizontalOptions = LayoutOptions.CenterAndExpand, 34 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 35 }; 36 37 Content = new StackLayout 38 { 39 Children = { 40 new StackLayout 41 { 42 Orientation = StackOrientation.Horizontal, 43 Padding = new Thickness(0,64,0,64), 44 Children = 45 { 46 btnIncrement, 47 btnDecrement 48 } 49 }, 50 lblDisplay 51 } 52 }; 53 } 54 55 private void BtnDecrement_Clicked(object sender, EventArgs e) 56 { 57 counter--; 58 lblDisplay.Text = counter.ToString(); 59 } 60 61 private void btnIncrement_Clicked(object sender, EventArgs e) 62 { 63 counter++; 64 lblDisplay.Text = counter.ToString(); 65 } 66 } 67 } ملاحظة تذكّر أنّنا نضيف صفحة محتوى جديدة بالنقر بزر الفأرة الأيمن على المشروع CounterApp (Portable) ثم نختار Add وبعدها New Item، ومن النافذة التي تظهر، تختار Cross-Platform من القسم الأيسر، ومن القسم الأيمن نختار Forms ContentPage لقد صادفنا العديد من التقنيّات المستخدمة في هذا التطبيق وذلك في الدروس السابقة. مع وجود بعض الأمور الجديدة. فمثلًا ولأوّل مرّة وضعنا مخطّط مكدّس ضمن آخر، وهذا أمر شائع كثيرًا في تصميم الواجهات. صرّحنا في السطر 8 عن الحقل الخاص counter وهو العدّاد الذي سيحتفظ بالقيمة الحالية للعدد المعروض ضمن اللصيقة، وأسندنا له القيمة الابتدائيّة 0. كما صرّحنا في السطر 9 عن الحقل الخاص lblCounter الذي سيمثّل اللصيقة التي سنعرض ضمنها قيمة العدّاد Counter. سنحتاج إلى كلّ من الحقلين السابقين ضمن توابع مختلفة من الصنف CounterPage لذلك وضعناهما على شكل حقلين خاصّين بهذه الصورة. صرّحنا ضمن البانية CounterPage عن المتغيّر btnIncrement من النوع Button وأسندنا إليه كائن جديد من نوع زر Button حيث أسندنا قيم الخصائص مباشرةً عند الإنشاء، ووظيفة هذا الزر زيادة قيمة المتغيّر counter بمقدار واحد وعرض النتيجة ضمن اللصيقة كما سنرى لاحقًا. لاحظ أنّنا في السطر 19 قد أسندنا معالج الحدث Clicked لهذا الزر. اسم هذا المعالج btnIncrement_Clicked وقد صرّحنا عنه في الأسفل في الأسطر من 61 حتى 65. تكرّر نفس الأمر من أجل الزر btnDecrement (الأسطر من 21 حتى 27) ومعالج الحدث الخاص به btnDecrement_Clicked (الأسطر من 55 حتى 59) ووظيفة هذا الزر هي إنقاص قيمة المتغيّر counter بمقدار واحد وعرض النتيجة ضمن اللصيقة. إذا انتقلنا إلى الخاصيّة Content للصنف CounterPage (السطر 37)، فنجد أنّه يتم إسناد مخطّط مكدّس جديد وهو يمتلك اتجاهًا رأسيًّا افتراضيًّا كما وسبق أن أوضحنا في درس سابق. سنُسند الخاصيّة Children له فحسب (السطر 39). تصرّح هذه الخاصيّة عن وجود ابنين لهذا المخطّط، الابن الأوّل هو مخطّط مكدّس آخر (الأسطر من 40 حتى 49)، والثاني هو اللصيقة lblDisplay التي ستعرض العدد الحالي: Children = { new StackLayout { Orientation = StackOrientation.Horizontal, Padding = new Thickness(0,64,0,64), Children = { btnIncrement, btnDecrement } }, lblDisplay } بالنسبة لمخطّط المكدّس الابن كما يظهر من الشيفرة الأخيرة، لاحظ أنّنا نضبط خاصيّة الاتجاه Orientation له لتكون أفقيّة StackOrientation.Horizontal، كما وضعنا حشوة Padding مناسبة من الأعلى والأسفل لمحتواه، وأخيرًا أسندنا الخاصيّة Children له بحيث تحتوي على ابنين هنا زرّي الزيادة btnIncrement والإنقاص btnDecrement كما هو واضح. إذًا سيحتوي المخطّط المكدّس الابن (الداخلي) على زرّين متموضّعين في الوسط أفقيًّا، وسيحتوي المخطّط المكدّس الأب (الخارجي) على مخطّط مكدّس ابن، وعلى لصيقة وهما متموضّعان رأسيًّا. انتقل إلى الملف App.cs واحرص على أن تكون بانية الصنف App على الشكل التالي: public App() { // The root page of your application MainPage = new CounterPage(); } عند تنفيذ البرنامج باستخدام F5 ستحصل على شكل شبيه بما يلي، علمًا أنّني أجريت عدّة نقرات على كلّ من الزرّين: تحسين تطبيق العدّاد سنُجري بعض التحسينات البسيطة على تطبيق العدّاد السابق. حيث سنعيد هيكلة الشيفرة البرمجيّة بحيث نستخدم تعابير Lambda كمعالجات أحداث، بدلًا من توابع مستقلّة ضمن الصنف، وفي ذلك فائدة كبيرة، حيث يجعل ذلك الشيفرة البرمجيّة سهلة القراءة والصيانة إلى حدٍّ كبير، كما يمكننا عند ذلك أن نجعل الحقلين counter و lblDisplay عبارة عن متغيّرين محليّين ضمن بانية الصنف. سنضيف ميّزة بسيطة أخرى إلى هذا التطبيق تتمثّل في أنّ البرنامج سيرفض جعل قيمة العدّاد سالبة، حيث سيعرض رسالة بهذا الخصوص، ولا يُغيّر قيمة العدّاد في هذه الحالة. أضف صفحة محتوى جديدة سمّها EnhancedCounterPage واحرص على أن تكون محتويات الملف EnhancedCounterPage.cs على الشكل التالي: 1 using Xamarin.Forms; 2 3 namespace CounterApp 4 { 5 public class EnhancedCounterPage : ContentPage 6 { 7 public EnhancedCounterPage() 8 { 9 int counter = 0; 10 11 Label lblDisplay = new Label 12 { 13 Text = "0", 14 TextColor = Color.Accent, 15 HorizontalOptions = LayoutOptions.CenterAndExpand, 16 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 17 }; 18 19 Button btnIncrement = new Button 20 { 21 Text = "+", 22 HorizontalOptions = LayoutOptions.CenterAndExpand, 23 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 24 }; 25 btnIncrement.Clicked += (s, e) => 26 { 27 counter++; 28 lblDisplay.Text = counter.ToString(); 29 }; 30 31 Button btnDecrement = new Button 32 { 33 Text = "-", 34 HorizontalOptions = LayoutOptions.CenterAndExpand, 35 FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 36 }; 37 btnDecrement.Clicked += (s, e) => 38 { 39 if (counter == 0) 40 { 41 DisplayAlert("تحذير", "لا يمكن لقيمة العدّاد أن تكون أصغر من الصفر", "موافق"); 42 } 43 else 44 { 45 counter--; 46 lblDisplay.Text = counter.ToString(); 47 } 48 }; 49 50 Content = new StackLayout 51 { 52 Children = { 53 new StackLayout 54 { 55 Orientation = StackOrientation.Horizontal, 56 Padding = new Thickness(0,64,0,64), 57 Children = 58 { 59 btnIncrement, 60 btnDecrement 61 } 62 }, 63 lblDisplay 64 } 65 }; 66 } 67 } 68 } جعلنا كل من الحقلين counter وlblDisplay عبارة عن متغيّرين محليّين (السطرين 9 و11). كما تجدر الملاحظة أنّ معالجات الأحداث التقليديّة قد اختفت، وحلّ محلّها تعابير Lambda. انظر إلى الزر btnIncrement في الأسطر من 25 حتى 29: btnIncrement.Clicked += (s, e) => { counter++; lblDisplay.Text = counter.ToString(); }; صرّحنا عن تعبير Lambda يقبل وسيطين s و e، ويعمل على زيادة قيمة المتغيّر counter بمقدار 1 ويعرض النتيجة ضمن اللصيقة. نفس الأمر تمامًا يسري على الزر btnDecrement. انظر إلى الأسطر من 37 حتى 48: btnDecrement.Clicked += (s, e) => { if (counter == 0) { DisplayAlert("تحذير", "لا يمكن لقيمة العدّاد أن تكون أصغر من الصفر", "موافق"); } else { counter--; lblDisplay.Text = counter.ToString(); } }; الفرق الوحيد هنا أنّنا نختبر قيمة المتغيّر counter في حال كان يساوي الصفر قبل إنقاصه، وبالتالي عرض رسالة مناسبة في حال كان كذلك من خلال التابع DisplayAlert (وهو من الصنف ContentPage وكان يمكن أن نصل إليه عن طريق الكلمة المحجوزة this أيضًا). يحتاج التابع DisplayAlert في أحد أشكاله (وهو الشكل المستخدم هنا) إلى ثلاثة وساط هي: عنوان الرسالة، والنص المعروض ضمنها، والنص المعروض على زر الإلغاء، على الترتيب. توجد أشكال أخرى سنتناولها تباعًا ضمن هذه السلسلة. انتقل إلى الملف App.cs وتأكّد أنّ بانية الصنف App على الشكل التالي: public App() { // The root page of your application MainPage = new EnhancedCounterPage(); } نفّذ البرنامج وانقر الزر (-) مباشرةً أي عندما تكون القيمة الظاهرة على اللصيقة هي صفر، مما سيؤدّي إلى ظهور رسالة التنبيه كما في الشكل التالي: الخلاصة تناولنا في هذا الدرس مبادئ التعامل مع الأزرار في Xamarin.Forms من خلال تطبيقين أساسيّين يتفاعلان مع المستخدم. بالإضافة إلى الحديث عن كيفيّة عرض رسائل مخصّصة للمستخدم باستخدام التابع DisplayAlert. هناك العديد من المزايا التي تتمتّع بها الأزرار في Xamarin.Forms. سنستعرض لاحقًا في هذه السلسلة للمزيد من المزايا الإضافيّة للأزرار بالإضافة إلى عناصر مرئيّة أخرى يمكن للمستخدم أن يتفاعل معها.
  16. سنكمل مسيرتنا في السلسلة التعليميّة حول ماجنتو. فبعدما تعلّمنا في الدرس السابق كيفية تنصيب وتجهيز ماجنتو على حاسوب يشغّل نظام التشغيل Ubuntu، حان الوقت الآن لنتعرّف على أنواع المنتجات التي يدعمها ماجنتو. يدعم ماجنتو في الواقع ستة أنواع من المنتجات، قد لا يتطلّب عملك التعامل معها جميعًا، ولكن من الضروري أن تتعرّف عليها وتفهم مميّزاتها بشكل جيّد بحيث تختار ما يناسبك منها. هذه الأنواع هي: المنتج البسيط Simple Product. المنتج المجموعة Grouped Product. المنتج القابل للضبط Configurable Product. المنتج الافتراضي Virtual Product. المنتج الحزمة Bundled Product. المنتج القابل للتنزيل Downloadable Product. سنتحدث قليلًا عن كلّ منتج مع ضرب أمثلة على ذلك. ثمّ نتحدّث عن موضوع مهم مشترك بين جميع أنواع المنتجات، ألا وهو سمات المنتج product attributes. المنتج البسيط Simple Product وكما يوحي الاسم، فهو منتج منفرد بنفسه، يمكن بيعه بشكل منفرد، أو كجزء من مجموعة منتجات. من الممكن أحيانًا أن يرافق المنتج البسيط مجموعة من الخيارات. حين ذلك يمكن أن نسميه بالمنتج المركّب. كمثال عن المنتج البسيط، يمكن اعتبار عدسة كاميرا تصوير رقميّة كمنتج بسيط. المنتج المجموعة Grouped Product بالنسبة للمنتج المجموعة فهو عبارة عن مجموعة من المنتجات البسيطة التي تُباع دفعة واحدة، وعادةً ما تكون هناك علاقة بين المنتجات في هذه المجموعة، ومن الممكن أيضًا أن يكون هناك عرض تخفيض في السعر عليها. كمثال على ذلك يمكن اعتبار المنتجات البسيطة التالية كمنتج مجموعة: عدسة كاميرا رقميّة، وكاميرا رقميّة، وبطاقة تخزين. المنتج القابل للضبط Configurable Product يبدو المنتج القابل للضبط كأنّه منتج بسيط ولكن مع قائمة تحوي خيارات مختلفة لكلّ متغيّر من متغيرات هذا المنتج. فمثًلا القميص المسمّى T-Shirt الذي يحمل علامةً ما، له متغيرين (سمتين) أساسيّين: اللون والقياس. بالنسبة للّون من الممكن أن تكون هناك قائمة بالألوان المتاحة لهذا القميص، وبالنسبة للقياس فتكون هناك قائمة أخرى كذلك بالقياسات المتوفّرة لهذا القميص. المهم في الموضوع أنّ أيّ تغيير في أيّ خيار من الخيارات المتوفّرة في القائمتين السابقتين، سيؤدّي إلى منتج "بسيط" مستقل تمامًا ضمن المخزون inventory. المنتج الافتراضي Virtual Product المنتج الافتراضي هو منتج ليس له وجود من الناحية الفيزيائيّة ولا يمكن شحنه كما في المنتجات السابقة. قد يكون هذا المنتج عبارة عن خدمة، أو عضوية في أحد المواقع مثلًا، أو قد يكون كفالة، أو حتى اشتراك، وغيرها من المنتجات ذات الطبيعة غير الماديّة. يمكن أن تُباع المنتجات الافتراضيّة بشكل منفصل، أو أن تكون مضمّنة كجزء من منتج مجموعة Grouped Product أو منتج حزمة Bundle Product. المنتج الحزمة Bundled Product لاحظنا أنّ المنتج المجموعة لا يُعطي المستخدم أيّ خيار حول المنتجات التي يوفرها. فإمّا أن تشتري المجموعة كلّها بسعر خاص أو لا. ففي المثال الذي أوردناه قبل قليل، كانت المجموعة عبارة عن عدسة كاميرا رقميّة، وكاميرا رقميّة، وبطاقة تخزين. لا يمكن للمشتري اختيار أيّ جزءٍ منها على حدة حتى ولو كان ليس بحاجة إليها جميعها. بالنسبة للمنتج الحزمة فهو يسمح للمشتري ببناء المجموعة الخاصة به باستخدام خيارات متعدّدة، ففي مثال الكاميرا وملحقاتها، من الممكن أن يبني المشتري منتج حزمة مكوّن من كاميرا وعدسة وحقيبة خاصّة بالكاميرا دون بطاقة التخزين مثلًا. وفي هذه الحالة من الممكن أيضًا أن نجري له حسمًا على هذا المنتج الحزمة الذي قام بتصميمه شخصيًّا وذلك حسب معايير محدّدة يمكن ضبطها. المنتج القابل للتنزيل Downloadable Product المنتج القابل للتنزيل هو أيّ منتج يمكن تسليمه كملف، مثل كتاب إلكتروني أو ملف صوتي أو ملف فيديو أو حتى تطبيق برمجي. كما يمكنك توفير نسخة عرض من هذا المنتج sample product كون أنّ عمليّة التنزيل لا تتم إلّا بعد دفع ثمن المنتج. تكون نسخة العرض عبارة عن جزء من كتاب، أو جزء من ملف صوتي، أو نسخة تجريبيّة من تطبيق بحيث تكون محدودة الإمكانيّات. من الممكن في ماجنتو أن يتمّ ضبط المنتجات القابلة للتنزيل بحيث تطلب من المستخدم أن يسجّل الدخول في الموقع، ومن ثمّ ترسل له رابط التحميل إلى بريده الإلكتروني على سبيل المثال. سمات منتج Product Attributes يعطينا ماجنتو مرونة عالية في ضبط المنتجات وسماتها. فمن الممكن مثلًا أن يكون لمنتجٍ ما سمات مثل اللون Color والحجم Size والوزن Weight وغيرها الكثير. يسمح ماجنتو بإسناد مثل هذه السمات إلى المنتجات عند إنشاءها. كما يسمح لنا بإنشاء سمات جديدة قد لا تكون موجودة أصلًا في ماجنتو. لتوضيح هذه الفكرة سنعمل على إضافة سمة جديدة اسمها نوع التغليف Packing Kind والتي تعبّر عن كيفيّة تغليف منتج ما: تغليف عادي Normal أم تغليف هدية Gift. افتح متصفّح الويب ثم انتقل إلى صفحة مدير النظام في ماجنتو: http://ubuntu/magento/admin. سجّل دخولك بحساب مدير النظام الذي أنشأته عند تنصيب ماجنتو لتصل بعدها إلى الصفحة الرئيسيّة Dashboard كما في الشكل التالي: من القائمة اليسرى، انقر STORES فتظهر لك نافذة جانبيّة صغيرة اختر منها البند Product تحت التصنيف Attributes. انظر الشكل التالي: سينقلك ذلك إلى جميع السمات attributes الخاصة بمنتجات هذا المتجر. كما في الشكل التالي: انقر الزر Add New Attribute ذو اللون البرتقالي الذي يظهر في الأعلى كما في الشكل السابق، وذلك لإضافة سمة جديدة. اعمل على تتبّع الخطوات التي ستظهر في الشكل التالي لإضافة سمة ستظهر بشكل قائمة منسدلة Dropdown وتحوي خيارين: عادي Normal وهدية Gift: لاحظ أنّه في الخطوة 1 اخترنا اسم لهذه السمة، وفي الخطوة 2 اخترنا القائمة المنسدلة Dropdown ليكون شكل السمة عند الإظهار. في الخطوة 3 حدّدنا أنّها سمة إلزامية وليست اختيارية. أمّا بالنسبة للعناصر التي ستظهر في القائمة فعليك اتّباع الخطوة 4 في النقر على زر إضافة خيار (عنصر) Add Option إلى القائمة ثم الخطوة 5 (لا تنسى نقر صندوق الاختيار بجوار اسم Normal ليكون هو الخيار الافتراضي)، ثم تنقر مرة أخرى على الزر Add Option ثم الخطوة 6 لإضافة الخيار (العنصر) الثاني. أخيرًا يمكننا حفظ هذه السمة بالنقر على الزر Save Attribute (الخطوة 7) في الأعلى. نكون قد انتهينا بذلك من إضافة هذه السمة. سنعود مجدّدا إلى الحديث عن السمات، عندما نبدأ بإضافة المنتجات في الدروس اللاحقة. ملاحظة في بعض الأحيان وبعد إضافة سمة جديدة قد تظهر لك رسالة تخبرك بوجوب الانتقال إلى إدارة الذاكرة المخبئيّة Cache Management لإجراء عمليّة تحديث refresh. انظر الشكل التالي: في هذه الحالة يمكنك النقر على رابط Cache Management الذي يظهر في الشكل السابق للانتقال إلى المكان المطلوب، ومن ثمّ تنقر الزر Submit لإجراء هذا التحديث كما في الشكل التالي: الخلاصة تعرّفنا في هذا الدرس على أنواع المنتجات التي يدعمها ماجنتو، كما تعرّفنا على مفهوم سمات attributes منتج وعملنا على إنشاء سمة بسيطة يمكن ربطها بأيّ منتج فيما بعد. يُعتبر التمييز بين أنواع المنتجات، واختيار ما هو مناسب للعمل التجاري أمرًا مهمًّا. سنتابع عملنا في هذه السلسلة، وسنتعرّف على في الدرس القادم على كيفيّة إعداد الأصناف Categories والمنتجات Products في ماجنتو.
  17. إذا كنت مصمّمًا على البدء في برمجة تطبيقات تعمل على iOS فستحتاج حكمًا إلى جهاز ماك. والسبب أنّ بيئة التطوير التي ستستخدمها وهي Xcode بالإضافة إلى حزمة التطوير SDK الخاصة بتطوير هذه التطبيقات يمكن استخدامها من خلال نظام تشغيل Mac OS X. توجد ثلاثة حلول برأيي: 1- أن تستخدم نسخة معدّلة من أحد أنظمة تشغيل Mac OS X لتعمل على ويندوز ضمن الآلة الافتراضيّة VirtualBox. وهذه الطريقة برأيي ليست مضمونة تمامًا وقد تحدث بعض المشكلات وخصوصًا عند تطوير التطبيقات، وقد لا تحدث مثل هذه المشكلات. انظر إلى الرابط التالي لتعرف كيف ذلك: http://www.instructables.com/id/How-to-install-OS-X-Mountain-Lion-on-your-PC-with-/ 2- أن تحاول شراء جهاز ماك مستعمل ونظيف بسعر مقبول، وهناك أيضًا حسبما رأيت أجهزة ماك مُعاد تصنيعها refurbished. 3- أن تشتري جهاز ماك ميني. سعره يبدأ بـ 499 دولار، وهناك نماذج متطوّرة أكثر منه. انظر الرابط التالي http://www.apple.com/mac-mini/specs/
  18. لا أنصحك مطلقًا بالتفكير في مثل هذه الطريقة. المجالين اللذين تتحدّث عنهما منفصلين تمامًا ولا يجوز الخلط بهما. إذا كنت تمتلك أحد حواسيب ماك، ولديك جهاز آيفون أو آيباد (ويمكن ألّا تمتلك أيًّا منهما حاليًّا)، ولديك معرفة برمجيّة أوليّة فيمكنك البدء بتعلّم لغة سويفت Swift لتطوير تطبيقات تعمل على iOS. أمّا إذا لم تكن تمتلك حاسوب ماك، ولم تكن مستعدًّا حاليًّا لدفع ثمنه، فالأفضل أن تتجه إلى اتجاه آخر، كتطوير تطبيقات أندرويد مثلًا. هذا بالنسبة لموضوع تطوير تطبيقات تعمل على الأجهزة المحمولة. أمّا بالنسبة لتطوير تطبيقات ويب، فالموضوع متشعّب وهناك الكثير مما يمكن قوله. أحتاج أولًا أن أعرف ماهو وضعك حاليًّا لكي أستطيع تقديم النصيحة الأفضل إن شاء الله.
  19. أهلًا أخي محمد. سعيد أنّك استفدت من هذا المقال، وأنا جاهز لأي استفسار حوله. يمكنك زيارة الرابط التالي للاطلاع دومًا على جديد سلسلة تعليم سي شارب: https://academy.hsoub.com/tags/تعلم سي شارب/
  20. سنتناول في هذا الدرس تطبيقين عمليّين حول كيفيّة التعامل مع القياسات في Xamarin. لقد تناولنا مفهوم القياسات في Xamarin.Forms في الدرس السابق من هذه السلسلة، وأوضحنا أهميّة هذا المفهوم في التنسيق الصحيح لواجهة المستخدم عند استخدام شاشات متنوّعة لأجهزة أندرويد. سيكون التطبيق الأوّل بسيطًا للغاية، حيث ينحصر عمله في رسم مستطيل صغير بالأبعاد الحقيقيّة، وإظهاره بنفس المساحة على مختلف أنواع الشاشات. أمّا التطبيق الثاني فهو عبارة عن ساعة رقميّة بسيطة يتغيّر حجمها بحسب حجم الشاشة، وبحسب وضع الشاشة أيضًا (أفقي أم عمودي). تطبيق رسم المستطيل أنشئ تطبيقًا جديدًا سمّه RectangleApp من النوع Blank App (Xamarin.Forms Portable)، وكما جرت العادة أبقِ على المشروعين RectangleApp (Portable) وRectangleApp.Droid فقط. ثم أضف صفحة محتوى جديدة سمّها RectanglePage واحرص على أن تكون بانيتها على الشكل التالي: public RectanglePage() { BoxView rectangle = new BoxView { HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, BackgroundColor = Color.Aqua, WidthRequest = 1 * 64, HeightRequest = 3 * 64 }; Content = rectangle; } الشيفرة البرمجيّة الموجودة في البانية RectanglePage بسيطة. حيث ننشئ كائنًا جديدًا من الصنف BoxView وهو عنصر مرئي تنحصر وظيفته في إكساب ناحية جماليّة لواجهة المستخدم. ضبطنا الخاصيّتين HorizontalOptions وVerticalOptions ليظهر هذا العنصر في وسط الشاشة. كما أسندنا الخاصيّتين WidthRequest وHeightRequest له كي نعيّن العرض والارتفاع على الترتيب. لاحظ هنا أنّه كان من الممكن أن يكون اسميّ كل من الخاصيتين السابقتين على الشكل Width وHeight فحسب. ولكن تمّ إضافة الكلمة Request لكلٍّ منهما، لكي يوحي ذلك أنّ القيم التي نُسندها إليهما قد لا يتمّ الالتزام بها بشكل دقيق. لاحظ أنّنا نُسند القيمة 1 * 64 لخاصيّة WidthRequest وهذا يعني بالطبع القيمة 64 بالواحدات المستقلّة عن الجهاز، أمّا الخاصيّة HeightRequest فقد أسندنا إليها القيمة 3 * 64 وهذا يعطينا 192 وحدة مستقلة عن الجهاز. تذكّر معي من الدرس السابق أنّ كل 64 وحدة مستقلة عن الجهاز تعادل 1 سنتيمتر، إذًا نرغب بأن يكون عرض المستطيل 1 cm وطوله 3 cm. انتقل إلى الملف App.cs وتأكّد أنّ بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new RectanglePage(); } نفّذ البرنامج باستخدام المفتاح F5 لتحصل على الشكل التالي: حاول أن تستخدم مسطرة عاديّة وتقيس أبعاد هذا المستطيل على الشاشة مباشرةً، ستجد أنّ أبعاده قريبة من الأبعاد التي حدّدناها في الشيفرة (1 cm x 3 cm). إذا نفّذت هذا التطبيق على أيّ جهاز محمول ستحصل على نفس الشكل تقريبًا. إذًا استطعنا رسم نفس الشكل تقريبًا باستخدام الواحدات المستقلّة عن الجهاز وبصرف النظر عن دقّة الشاشة المستخدمة. تطبيق الساعة الرقميّة سيكون هذا التطبيق متقدّمًا أكثر ويحتوي على مفاهيم مفيدة في البرمجة بشكل عام. الغاية من هذا التطبيق هو إنشاء ساعة رقميّة ذات تصميم بسيط تعرض الوقت بشكل آني كلّ ثانية. الميزة فيها أنّها ستشغل كامل عرض الشاشة تقريبًا بصرف النظر عن الجهاز المستخدم أو وضع تدوير الشاشة (أفقي أم عمودي). أي ستكون الساعة مرنة لتتناسب مع أيّ شاشة يعمل عليها التطبيق. أنشئ تطبيقًا جديدًا سمّه DigitalClockApp من النوع Blank App (Xamarin.Forms Portable)، وأبقِ على المشروعين DigitalClockApp (Portable) وDigitalClockApp.Droid فقط. ثم أضف صفحة محتوى جديدة سمّها DigitalClockPage. عدّل محتويات الملف DigitalClockPage.cs ليبدو على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace DigitalClock 5 { 6 public class DigitalClockPage : ContentPage 7 { 8 private Label lblClock; 9 10 public DigitalClockPage() 11 { 12 lblClock = new Label 13 { 14 HorizontalOptions = LayoutOptions.Center, 15 VerticalOptions = LayoutOptions.Center 16 }; 17 18 SizeChanged += DigitalClockPage_SizeChanged; 19 20 Device.StartTimer(TimeSpan.FromSeconds(1), OneSecondPassed); 21 22 Content = lblClock; 23 } 24 25 private void DigitalClockPage_SizeChanged(object sender, EventArgs e) 26 { 27 if (this.Width > 0) 28 { 29 lblClock.FontSize = this.Width / 10.0 * 2; 30 } 31 } 32 33 private bool OneSecondPassed() 34 { 35 lblClock.Text = DateTime.Now.ToString("h:mm:ss tt"); 36 37 return true; 38 } 39 } 40 } انتقل إلى الملف App.cs واحرص على أن تكون بانية الصنف App.cs على الشكل التالي: public App() { // The root page of your application MainPage = new DigitalClockPage(); } نفّذ البرنامج باستخدام F5 ستحصل على شكل شبيه بما يلي: لنبدأ الآن بمناقشة هذا التطبيق. صرّحنا في السطر 8 عن حقل خاص من نوع Label لصيقة اسمه lblClock، وقمنا في السطر 12 ضمن بانية الصنف DigitalClockPage بإنشاء كائن جديد وأسندناه إلى هذا الحقل. سنستخدم في هذا التطبيق وللمرّة الأولى حدثين. الأوّل هو الحدث SizeChanged للصفحة DigitalClockPage والثاني هو الحدث الذي ينتج من استخدام التابع الساكن StartTimer من الصنف Device، وهو حدث يتمّ تفعيله بعد مرور فترة زمنيّة يمكن تحديدها سلفًا أي أنّه سيلعب دور مؤقّت زمني. بالنسبة للحدث SizeChanged فقد عيّنا معالج حدث له في السطر 18. اسم معالج الحدث هو DigitalClockPage_SizeChanged وقد صرّحنا عنه في الأسطر من 25 حتى 31. تحدثنا عن الحدث SizeChanged في الدرس السابق، وكيف أنّه يُفعَّل كلّما تغيّر عرض أو ارتفاع العنصر المرئي المُسنَد إليه معالج الحدث. إذًا كلّما يتغيّر عرض أو ارتفاع الصفحة سيتم استدعاء معالج الحدث DigitalClockPage_SizeChanged. يتم في البداية ضمن معالج الحدث هذا اختبار كون عرض الصفحة أكبر من الصفر أم لا وذلك في السطر 27، لأنّ عرض الصفحة من الممكن أن يحمل قيمة تمهيديّة سالبة (-1) وهي قيمة لا نرغب بالتعامل معها بالطبع. يتم بعد ذلك ضبط حجم النص المُستخدَم والملائم للشاشة التي يعمل عليها التطبيق، وإليك كيف يحدث ذلك. بمعزل عن Xamarin، يقيس حجم النص font size عادةً المسافة الرأسيّة المشغولة بين خطّين وهميين، يقع الخط الأوّل فوق أعلى النص بقليل، أمّا الخط الثاني فيقع تحت أسفل النص بقليل. الواحدة المستخدمة مع حجم الخط هي النقطة Point. حيث يُعتبر أنّ كل بوصة تحتوي على 72 نقطة. فالخط الذي قياسه 72 مثلًا يعني بأنّ ارتفاع أي حرف ضمنه لن يتجاوز البوصة الواحدة. أمّا بالنسبة لعرض أي حرف، فهو يساوي تقريبًا نصف قياس الخط المستخدم. فالخط الذي قياسه 12 نقطة مثلًا، سيكون عرض أي حرف فيه يساوي 6 نقاط تقريبًا. يبقى الكلام السابق صحيحًا تمامًا عند البرمجة في Xamarin. ففي تطبيقنا هذا، سيُعرَض على الشاشة 9 أو 10 محارف بحسب الساعة الحالية. تتضمّن أرقام الساعة وعلامتي (:) بالإضافة إلى فراغ واحد وحرف واحد (“م” أو “ص”) يُشير إلى المساء أو الصباح. سنعتبر عدد المحارف 10 وهكذا فعندما نقسّم عرض الصفحة Width على عدد المحارف، سينتج معنا عرض المحرف الواحد من عرض الصفحة. وبما أنّ قياس الخط هو ضعف عرض المحرف تقريبًا كما وسبق أن أشرنا قبل قليل، لذلك يؤدي ضرب عرض المحرف الواحد بالعدد 2 إلى إيجاد قيمة تقريبيّة ولكنّها مقبولة إلى حدٍّ كبير لحجم الخط مُقاسًا بالواحدات المستقلّة، وهذا ما تُعبّر عنه العبارة البرمجية في السطر 29. وهكذا يتم ضبط حجم الخط للساعة الرقميّة ليتلاءم تمامًا مع عرض الصفحة على أيّ جهاز محمول. بالنسبة للحدث الذي يتمّ تفعليه كل ثانية، فنحصل عليه من استدعاء التابع الساكن StartTimer من الصنف Device كما أسلفنا. يتطلّب هذا التابع وسيطين الأوّل هو الفترة الزمنيّة اللازمة لكي يتم تفعيل هذا الحدث، وهذا الوسيط من النوع TimeSpan وهي بنية struct تمثّل فترة زمنيّة محدّدة. وبما أنّنا نريد أن يتم تفعيل الحدث كل ثانية، لذلك فاستخدامنا التابع الساكن FromSeconds من البنية TimeSpan على الشكل التالي: TimeSpan.FromSeconds(1) الذي يُرجع قيمة من البنية TimeSpan تمثّل ثانية واحدة فقط لأنّنا مرّرنا القيمة 1 لهذا التابع. أمّا الوسيط الثاني فهو عبارة عن نائب delegate يُعبّر عن معالج هذا الحدث. هذا النائب هو من النوع Func وهو نوع نائب موجود ضمن نطاق الاسم System، يُغلّف هذا النائب تابع لا يتطلّب أيّ وسيط، ولكن يُرجع قيمة من النوع bool. وهذا ما يتحقّق بالنسبة لمعالج الحدث OnSecondPassed الذي أنشأناه خصيصًا لهذا الغرض (الأسطر من 33 حتى 38). ومرّرنا اسمه كوسيط ثانٍ إلى التابع StartTimer. لاحظ أنّ معالج الحدث OnSecondPassed يُرجع دومًا القيمة true (السطر 37) وهو شرط استمرار عمل المؤقّت الزمني. نلاحظ في السطر 35 كيف يتم إسناد النص التنسيقي الذي يمثّل الوقت الحالي إلى الخاصية Text من اللصيقة lblClock. استخدمنا البنية DateTime للحصول على الوقت الحالي من خلال الخاصيّة Now. ثمّ استدعينا التابع ToString من Now ومرّرنا له نصًّا تنسيقيًّا للحصول على الوقت الحالي بالشكل الذي يلائم تطبيقنا: enter code hereDateTime.Now.ToString(“h:mm:ss tt”); إذا انتبهت إلى النص التنسيقي ستجد أنّنا قد كتبنا الحرف h الذي يُعبّر عن الساعة مرّة واحدة فقط، وسبب ذلك أنّني لا أريد من البرنامج أن يحجز خانتين في حال كانت الساعة قبل العاشرة (أي مكوّنة من خانة واحدة فقط). أمرٌ آخر، يُشير الحرفان tt إلى الوقت الحالي هل هو صباحًا أم مساءً. يظهر من صورة التطبيق عندما نفّذته على جهازي أنّ هناك حرفًا واحدًا فقط يظهر وهو “م” أي مساءً (وربما يظهر “ص” صباحًا حسب وقت التجريب). سبب ظهور هذا الشكل من الأحرف هو الإعدادات الإقليميّة الموجودة على جهازي. من الممكن أن نستخدم في تطبيقنا الحرفين AM بدلًا من “ص” أو أن نستخدم الحرفين PM بدلًا من “م” ولكنّنا نحتاج إلى تغيير الإعدادات الإقليمية الافتراضيّة في هذه الحالة. سنجري تعديلًا على تطبيقنا هذا بحيث يعتمد الحرفين AM (قبل الظهر – صباحًا) أو الحرفين PM (بعد الظهر – مساءً). أجرِ التعديل البسيط التالي على العبارة البرمجيّة الموجودة في السطر 35 لتصبح على الشكل التالي: lblClock.Text = DateTime.Now.ToString("h:mm:ss tt", CultureInfo.InvariantCulture); لقد مرّرنا وسيطًا إضافيًّا إلى التابع ToString (يخضع لزيادة التحميل overloading) يضمن بأن يستخدم هذا التابع الإعدادات الإقليميّة العامّة InvariantCulture (تشبه تلك الخاصّة بالولايات المتحدّة) عند تنسيق النص. هناك أمرٌ صغير آخر، لاحظ أنّ تطبيقنا سيستخدم محرفًا إضافيًّا لعرض الساعة، لأنّ AM أو PM مكوّنة من حرفين، وليس من حرف واحد كما كان عليه الحال مع “م” أو “ص”. هذا بالإضافة إلى كون AM أو PM عبارة عن أحرف طباعيّة كبيرة لذلك ستأخذ عرضًا أكبر من باقي المحارف. لذلك لا مشكلة إذا اعتبرنا أنّه لدينا الآن 13 محرف كحد أقصى بدلًا من 10 كما كان عليه الحال في النسخة القديمة من هذا التطبيق، وهي في الحقيقة قيمة تجريبيّة حصلت عليها بالتجريب. لذلك سنجري تعديلًا بسيطًا آخر في العبارة البرمجيّة في السطر 29 حيث سنستبدل القيمة 13 بالقيمة القديمة 10 كما يلي: lblClock.FontSize = this.Width / 13.0 * 2; كما ستحتاج أيضًا إلى إضافة نطاق الاسم System.Globalization في الأعلى. إذا جرّبت تنفيذ البرنامج الآن ستحصل على شكل شبيه بما يلي: جرّب أن تدوّر الشاشة، سترى أن النص سينسجم تمامًا مع هذه الحالة، ويملأ كامل عرض الشاشة بشكل جميل. الخلاصة تناولنا في هذا الدرس تطبيقين عمليين يوضّحان كيفيّة التعامل مع واحدات القياس المستقلّة عن الجهاز independent-device units، كما تعرّفنا على مفاهيم برمجيّة جديدة تتمثّل في التعامل مع المؤقّت الزمني، وفي التعامل مع الإعدادات الإقليميّة. يُعتبر هذا الدرس تطبيقًا عمليًّا للدرس السابق يمكن اللجوء إليه لفهم أفضل حول هذا الموضوع.
  21. نتابع عملنا في هذه السلسلة بتنصيب ماجنتو على نظام التشغيل Ubuntu 16.04.1 LTS. لقد اخترت طريقة سهلة وعمليّة لتنصيب ماجنتو واستثماره. يعتمد ماجنتو في عمله على وجود خادوم الويب Apache بالإضافة إلى لغة PHP وقواعد بيانات MySQL. سنستخدم الإصدار الأخير من ماجنتو وهو 2.1. يحتاج هذا الإصدار إلى الإصدارات التالية من البرمجيّات السابقة: Apache 2.2 أو Apache 2.4. MySQL 5.6 PHP 5.6.x حيث من الممكن أن يكون x أي رقم. لا يُعتبر تجهيز البرمجيات السابقة بالشكل الملائم أمرا يسيرا في الواقع. لذلك فقد آثرت استخدام توزيعة اسمها XAMPP، وهي توزيعة مشهورة تسمح بتنصيب البرمجيّات السابقة (وأكثر) دفعةً واحدةً. تأتي توزيعة XAMPP من مجموعة تُدعى Apache Friends. المثير في الأمر أنّ هذه المجموعة تتعاون مع شركة اسمها Bitnami وهي شركة متخصّصة بالحوسبة السحابيّة، حيث توفّر هذه الشركة العديد من التطبيقات المهمّة التي تعمل بشكل سلس على توزيعة XAMPP. من بين هذه التطبيقات هو تطبيق ماجنتو Magento. يمكننا بإجراءات بسيطة للغاية تجهيز ماجنتو وتشغليه على XAMPP. سنبدأ بتحميل وتنصيب توزيعة XAMPP وما تحويه من برمجيّات ضروريّة لعمل ماجنتو، ثمّ سنعمل على تحميل وتنصيب تطبيق ماجنتو. يفترض هذا الدرس أنّه لديك خبرة أساسيّة بطريقة التعامل مع أنظمة تشغيل Linux بشكل عام، مثل استخدام الطرفية Terminal والوصول عن طريقها إلى المجلّدات المختلفة، وتنفيذ الأوامر. تحميل وتنصيب توزيعة XAMPP انتقل إلى الحاسوب الذي يشغّلUbuntu . ثم استخدم المتصفّح للانتقال إلى موقع التحميل الخاص بتوزيعة XAMPP. استخدم شريط التمرير لتصل إلى القسم الخاص بلينوكس كما في الشكل التالي: حمّل النسخة التي تتضمّن PHP 5.6.23 (في الوسط) مع معمارية 64 بت. عند الانتهاء من تحميل الملف، انتقل إلى الطرفية Terminal ومنها إلى المكان الذي حمّلت إليه الملف السابق. بالنسبة إليّ كان الملف يحمل الاسم التالي وهو موجود ضمن المجلّد Documents: xampp-linux-x64-5.6.23-0-installer.run نفّذ الأمرين التاليين: sudo chmod 755 xampp-linux-x64-5.6.23-0-installer.run sudo ./xampp-linux-x64-5.6.23-0-installer.run كما يظهر في الشكل التالي: لاحظ أنّ الطرفية تطلب إدخال كلمة المرور الخاصة بالمستخدم الحالي بعد تنفيذ الأمر الأوّل مباشرةً، وذلك بسبب استخدام الأمر sudo. بعد تنفيذ الأمر الثاني سيبدأ برنامج الإعداد الخاص بالتوزيعة XAMPP بالعمل، حيث سيُظهر رسالة ترحيبيّة كما في الشكل التالي: انقر الزر Next للمتابعة لتصل إلى النافذة الخاصة بالمكوّنات الأساسيّة التي سيتم تنصيبها كما يلي: تأكّد من صناديق الاختيار، ثم انقر الزر Next. ستخبرك النافذة التالية عن المكان الذي سيتم فيه حفظ الملفات وهو /opt/lampp انقر زر Next لتحصل على نافذة دعائيّة للخدمات التي من الممكن أن تحصل عليها مع Bitnami. تابع نقر Next حتى تبدأ عملية التنصيب. ستأخذ عملية التنصيب قليلًا من الوقت، وبعد أن تنتهي، ستحصل على نافذة شبيهة بما يلي: احرص على اختيار تشغيل XAMPP من تحديد صندوق الاختيار إن لم يكن كذلك، ثم انقر الزر Finish ليعمل تطبيق الإدارة الخاص بـ XAMPP كما يظهر من الشكل التالي: انقر لسان التبويب Manage Servers من الأعلى لتحصل على شكل شبيه بما يلي: نلاحظ من الشكل السابق أنّ كل من الخادومين MySQL Database و Apache Web Server هما حاليًا في طور التشغيل Running. إن لم يكونا كذلك، فيمكنك تحديد كل منهما على حدة ونقر زر Start الذي يظهر في الجهة اليمنى من الشكل السابق. للتأكّد من نجاح عمليّة التنصيب السابقة، افتح متصفّح الانترنت لديك، ثم اكتب ضمن شريط العنوان ما يلي: http://ubuntu ثم اضغط الزر Enter، يجب أن تحصل على شكل شبيه بالشكل التالي: نكون عند هذه النقطة قد انتهينا من تنصيب XAMPP وبتنا مستعدّين لتنصيب ماجنتو. تحميل وتنصيب ماجنتو انتقل بمتصفّح الويب إلى العنوان التالي https://bitnami.com/stack/xampp لعرض التطبيقات التي توفرها Bitnami لتوزيعة XAMPP. ثم استخدام شريط التمرير حتى تصل إلى ماجنتو Magento كما في الشكل التالي: انقر زر Download الخاص بنظام التشغيل لينكس مع معمارية 64 بت كما في الشكل السابق لتبدأ عملية التحميل. بعد الانتهاء من التحميل، انتقل باستخدام الطرفية Terminal إلى المجلّد الذي يحوي الملف المُحمَّل. بالنسبة إليّ كان الملف يحمل الاسم التالي وهو موجود ضمن المجلّد Documents: bitnami-magento-2.1.0-2-module-linux-x64-installer.run نفّذ الأمرين التاليين ضمن الطرفية: sudo chmod 755 bitnami-magento-2.1.0-2-module-linux-x64-installer.run sudo ./bitnami-magento-2.1.0-2-module-linux-x64-installer.run كما في الشكل التالي: لاحظ من الشكل السابق أنّ الطرفية قد طلبت كلمة المرور للمستخدم الحالي، كما هو الحال عند تنصيب XAMPP وذلك لأنّني أعدت تشغيل الطرفية من جديد. بمجرّد تنفيذ الأمر الثاني سيبدأ عمل برنامج التنصيب الخاص بماجنتو، حيث ستظهر لك رسالة ترحيبيّة من برنامج الإعداد كما يلي: انقر Next ليعرض لك برنامج الإعداد نافذة تفيد بالمكوّنات المراد تنصيبها. يوجد مكوّن واحد هو ماجنتو Magento يجب أن يكون في حالة اختيار. انقر Next لتصل إلى النافذة التي تحدّد المسار الذي سيتم فيه نسخ الملفات. يجب أن يكون هذا المسار هو /opt/lampp انقر زر Next مرّة أخرى لتصل إلى النافذة التالية: ستطلب هذه النافذة بعض المعلومات الشخصيّة مثل الاسم المرغوب لتسجيل الدخول على ماجنتو (Login) والاسم الحقيقي والبريد الإلكتروني. كما ستطلب منك تعيين كلمة المرور الخاصة بتطبيق ماجنتو. بعد أن تنتهي من كتابة البيانات المطلوبة انقر Next لتصل إلى النافذة التالية: تطلب منك النافذة السابقة تحديد عنوان المضيف لإنشاء الروابط الداخلية. وهنا ينبغي أن تكتب العنوان الخاص بموقعك. في حالتنا هذه، استخدمت العنوان المحلّي 127.0.1.1 فحسب. يمكن بالتأكيد تغيير هذا العنوان فيما بعد. انقر الآن الزر Next لتصل إلى النافذة الخاصة بإعدادات خادوم البريد الصادر SMTP الذي سيستخدمه ماجنتو لإرسال رسائل البريد الإلكتروني. انظر الشكل التالي: لاحظ أنّني قد اخترت مزوّد خدمة GMail للبريد الصادر SMTP. أدخل البريد الإلكتروني الذي ترغب استخدامه مع هذه الخدمة، ثم زوّد برنامج الإعداد بكلمة المرور لهذا الحساب. انقر Next لتصل إلى نافذة تعرض عليك نشر deploy نسخة ماجنتو هذه إلى الخدمة السحابية الخاصة بشركة Bitnami كما في الشكل التالي: أزل الاختيار من الصندوق ثم انقر زر Next. سيعرض برنامج الإعداد رسالة تفيد بأنّه جاهز لبدء عمليّة التنصيب. انقر Next للبدء. سيستغرق الأمر بعض الوقت، وبعد الانتهاء سيعرض برنامج الإعداد رسالة تفيد بانتهاء عمليّة التنصيب ويعرض عليك تشغيل ماجنتو فورًا. كما في الشكل التالي: انقر على زر Finish لتختفي هذه النافذة وتظهر نافذة متصفّح الويب وقد انتقلت لتعرض الصفحة الرئيسيّة لمتجر ماجنتو: نكون بذلك قد انتهينا من تنصيب ماجنتو وبات جاهزًا للعمل. الخلاصة تعلّمنا في هذا الدرس كيفيّة تنصيب تطبيق ماجنتو بشكل عمليّ وسهل. حيث مررنا بخطوتين رئيسيتين. الأولى هي تنصيب توزيعة XAMPP التي تتضمن خادوم الويب Apache وخادوم قاعدة البيانات MySQL. أمّا الخطوة الثانية فكانت تنصيب ماجنتو من خلال برنامج إعداد مخصّص من Bitnami لكي ينصّب ماجنتو بوجود التوزيعة XAMPP. سنتابع عملنا في الدرس القادم في إلقاء نظرة على المنتجات في ماجنتو.
  22. لا تخلو أيّ لغة برمجة محترمة من وسيلة لمعالجة الأخطاء. تحتوي لغة سي شارب على آليّة قويّة لمعالجة الأخطاء. تشبه هذه الآلية إلى حدّ ما تلك المستخدمة في لغة Java. هناك نوعان أساسيّان من الأخطاء التي قد تنتج عن البرنامج: النوع الأوّل هي أخطاء أثناء التصميم، أي أثناء كتابة الشيفرة، وهي أخطاء يمكن اكتشافها بسهولة من خلال مترجم سي شارب الذي يتعرّف عليها ويعطيك وصفًا عنها، وقد يزوّدك أيضًا ببعض المقترحات للتخلّص منها. بالنسبة للنوع الثاني من الأخطاء فهي التي تنتج أثناء تنفيذ البرنامج runtime errors. توجد الكثير من الأسباب لحدوث مثل هذه الأخطاء. فمن القسمة على صفر إلى القراءة من ملف غير موجود أو حتى استخدام متغيّر مصرّح على أنّه من نوع مرجعيّ ولكن لم تتم تهيئته بعد بمرجع إلى كائن. عند حدوث مثل هذه الأخطاء ترمي بيئة التنفيذ المشتركة CLR استثناءً exception يحتوي على معلومات بخصوص الخطأ الذي حدث، فإذا كنت مستعدًّا لالتقاط هذا الاستثناء فستنجو، وإلّا سيتوقّف برنامجك عن العمل بصورة مفاجئة. التقاط استثناء من خلال عبارة try-catch إذا صادفتك عبارة برمجيّة تحتوي على عمليّة حسابيّة أو على استدعاء لتابع آخر، أو أيّ شيء قد يثير الريبة في نفسك، فمن الممكن مراقبتها أثناء تنفيذ البرنامج باستخدام عبارة try-catch. تتألّف هذه العبارة من قسمين: القسم الأوّل هو قسم المراقبة try، والقسم الثاني هو قسم الالتقاط catch. الشكل "الأبسط" لهذه العبارة هو التالي: try { //عبارة برمجيّة مريبة } catch(Exception exp) { //هنا يُلتقط الاستثناء وتتمّ معالجته } يمكن أن يحوي قسم try على عبارات برمجيّة بقدر ما ترغب. عندما يصادف البرنامج أثناء التنفيذ خطأً ما، سيتوقّف التنفيذ عند العبارة التي سبّبت الخطأ، ثم ينتقل فورًا إلى قسم الالتقاط catch. لاحظ معي أنّ قسم catch سيُمرَّر إليه وسيط من الصنف Exception. الصنف Exception موجود ضمن نطاق الاسم System، وهو الصنف الأب لجميع الاستثناءات. إذ أنّ أي استثناء مهما كانت صفته (بما فيها الاستثناءات التي يمكنك أن تكتبها أنت) يجب أن ترث من هذا الصنف. بعد حدوث الاستثناء والانتقال إلى قسم catch، سيحتوي الوسيط exp على معلومات حول مكان حدوث الاستثناء وسبب حدوثه، وغيرها من المعلومات التي قد تكون مفيدة لمستخدم البرنامج. تجدر الملاحظة بأنّ البرنامج لن يدخل إلى القسم catch أبدًا ما لم يحدث استثناء ضمن القسم try. لنرى الآن البرنامج Lesson16_01 الذي سيعرّفنا على الاستثناءات بشكل عمليّ. يحتوي هذا البرنامج البسيط على عبارة try-catch وحيدة سنعمل من خلالها على توليد خطأ بشكل مقصود أثناء التنفيذ، وسنتعلّم كيفيّة المعالجة. 1 using System; 2 3 namespace Lesson16_01 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int x = 5; 10 int y = 0; 11 int result; 12 13 try 14 { 15 result = x / y; 16 } 17 catch(Exception exp) 18 { 19 Console.WriteLine("The following error has occurred:"); 20 Console.WriteLine(exp.Message); 21 } 22 23 Console.WriteLine("Good Bye!"); 24 } 25 } 26 } من الواضح أنّ هذا البرنامج يتجّه لأن يجري عمليّة قسمة على صفر في السطر 15 ضمن القسم try. عند وصول البرنامج إلى السطر 15 وإجراء عمليّة القسمة هذه، سيتولّد استثناء يؤدّي إلى انتقال التنفيذ مباشرةً إلى قسم catch في السطر 17. في قسم catch يعرض البرنامج معلومات عن هذا الخطأ باستخدام الخاصيّة Message لكائن الحدث exp. في النهاية يعرض البرنامج في السطر 23 رسالة توديعيّة للمستخدم. نفّذ البرنامج لتحصل على الخرج التالي: The following error has occurred: Attempted to divide by zero. Good Bye! جرّب الآن تغيير قيمة المتغيّر y لتصبح 1 مثلًا وأعد تنفيذ البرنامج. ستلاحظ ظهور الرسالة التوديعيّة فقط على الشاشة، أي أنّه لم يحدث أي استثناء هذه المرّة. على كلّ الأحوال لا ينصح باستخدام معالجة الاستثناءات من أجل حالة القسمة على صفر في البرنامج السابق. ملاحظة: في الواقع يمكن الاستغناء عن الوسيط الذي يمرّر إلى قسم catch بشكل كامل، وفي هذه الحالة لن يكون بإمكانك الحصول على معلومات حول الاستثناء المُلتقط. سيبدو شكل عبارة try-catch على الشكل التالي: try { //عبارة برمجيّة مريبة } catch { //هنا يُلتقط الاستثناء وتتمّ معالجته } من الممكن استخدام هذا الأسلوب إذا كنّا على يقين حول طبيعة الخطأ الذي سيحدث. عبارة try-catch أكثر تطورا تصادفنا في بعض الأحيان حالات يكون من الضروري معها مراقبة أكثر من عبارة برمجيّة مريبة ضمن قسم try. لقد اتفقنا أنّه عند حدوث أيّ استثناء ضمن قسم try سينتقل التنفيذ إلى قسم catch. ولكن كيف سنميّز العبارة التي سبّبت هذا الاستثناء في قسم try؟ توجد العديد من الأصناف التي ترث من الصنف Exception والتي يُعتبر كلّ منها استثناءً مخصّصًا أكثر للمشكلة التي قد تحدث. فمثًلا كان من الممكن في البرنامج Lesson16_01 السابق أن نستخدم الصنف DivideByZeroException بدلًا من الصنف Exception في عبارة catch، وذلك لأنّه يرث (بشكل غير مباشر) من الصنف Exception، وسيعمل البرنامج كما هو متوقّع. ولكن في هذه الحالة لن تستطيع catch التقاط سوى الاستثناءات التي تنتج عن القسمة على صفر. في كثير من الحالات قد تتسبّب العبارات البرمجيّة الموجودة في قسم try باستثناءات متنوّعة لا توجد علاقة فيما بينها. مما يفرض علينا استخدام الصنف Exception لكي نلتقط بشكل مؤكّد أي استثناء قد يصدر عنها، أو أن تساعدنا سي شارب في هذا الخصوص! في الحقيقة الخيار الثاني هو الأفضل وهو جاهز. يمكننا في الواقع إضافة أقسام catch أخرى بقدر ما نرغب بعد قسم try. سيوضّح البرنامج Lesson16_02 هذه الفكرة من خلال فتح ملف نصي ومحاولة قراءة محتوياته. لاحظ أنّنا سنستخدم هنا نطاق الاسم System.IO. 1 using System; 2 using System.IO; 3 4 namespace Lesson16_02 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 string contents; 11 12 Try 13 { 14 using (StreamReader sr = new StreamReader("myfile.txt")) 15 { 16 contents = sr.ReadLine(); 17 } 18 19 Console.WriteLine("This file contains {0} characters.", contents.Length); 20 } 21 catch(NullReferenceException nullExp) 22 { 23 Console.WriteLine("The file does not contain any data."); 24 } 25 catch(FileNotFoundException notFoundExp) 26 { 27 Console.WriteLine("File: {0} not found!", notFoundExp.FileName); 28 } 29 } 30 } 31 } يستخدم هذا البرنامج قسميّ catch. القسم الأوّل (الأسطر من 21 إلى 24) يلتقط استثناءً من النوع NullReferenceException وهذا يحدث عند محاولة استدعاء تابع أو خاصيّة من متغيّر يحتوي على null بدلًا من مرجع لكائن حقيقي. أمّا القسم الثاني (الأسطر من 25 إلى 28) فهو يلتقط استثناءً من النوع FileNotFoundException والذي يحدث عند محاولة القراءة من ملف غير موجود. نفّذ البرنامج السابق وستحصل على الرسالة التالية في الخرج: File: C:\\Users\Husam\documents\visual studio 2015\Projects\Lesson16_02\Lesson16_02\bin\Debug\myfile.txt not found! وهذا طبيعي تمامًا لأنّني لم أنشئ الملف myFile.txt في هذا المسار. لاحظ كيف يضيف الصنف FileNotFoundException خاصيّة جديدة له وهي FileName (السطر 27) من النوع string التي تحوي مسار الملف مع اسمه. أنشئ الآن الملف myFile.txt واتركه فارغًا، ثمّ ضعه ضمن نفس المجلّد الذي يحوي الملف التنفيذي للبرنامج (موجود ضمن bin\Debug\). أعد تنفيذ البرنامج وستحصل على الرسالة التالي: The file does not contain any data. السبب في ظهور هذه الرسالة هو الاستثناء NullReferenceException وذلك لأنّنا حاولنا الوصول إلى الخاصيّة Length (تعطينا عدد المحارف الموجودة ضمن متغيّر نصي) من المتغيّر النصي contents رغم أنّه يحتوي على null (تذكّر بأنّنا تركنا الملف myFile.txt فارغًا). اذهب إلى الملف myFile.txt الذي أنشأناه قبل قليل، واكتب بعض الكلمات ضمنه واحفظ الملف، ثمّ أعد تنفيذ البرنامج Lesson16_02 من جديد. يجب الآن أن تحصل على رسالة تخبرك بعدد الأحرف التي كتبتها ضمن الملف. ملاحظة: يجب الانتباه إلى ترتيب أقسام catch. فلو كان مثلًا أوّل قسم catch موجود بعد قسم try يلتقط استثناءً من النوع Exception فعندها لن يستطيع أي قسم لاحق التقاط أي استثناء، لأنّ جميع الاستثناءات سيلتقطها هذا القسم الأوّل. السبب في ذلك أنّ الصنف Exception هو الأب العام لجميع أصناف الاستثناءات الأخرى، فيمكن له التقاطها. عبارة try-catch-final يمكن إضافة قسم أخير لعبارة try-catch اسمه final. وكما يوحي اسمه، فهذا القسم يمكن له أن يحتوي على عبارات برمجيّة سيتمّ تنفيذها بعد أن يدخل البرنامج إلى القسم try العائد له. وذلك سواءً أحدث استثناء ضمن try أم لم يحدث. تكمن فائدة وجود هذا القسم، في أنّه قد نواجه أحيانًا بعض الحالات التي تتطلّب إجراء بعض المهام عندما نفرغ من قسم try مثل إغلاق بعض المصادر المفتوحة، أو تحرير الذاكرة بشكل فوري وغيرها. الشرط الوحيد لاستخدام هذا القسم الاختياري هو أن يكون آخر قسم في عبارة try-catch. سنعدّل البرنامج Lesson16_02 ليدعم القسم final. انظر البرنامج Lesson16_03. 1 using System; 2 using System.IO; 3 4 namespace Lesson16_03 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 string contents; 11 12 try 13 { 14 using (StreamReader sr = new StreamReader("myfile.txt")) 15 { 16 contents = sr.ReadLine(); 17 } 18 Console.WriteLine("This file contains {0} characters.", contents.Length); 19 } 20 catch(NullReferenceException nullExp) 21 { 22 Console.WriteLine("The file does not contain any data."); 23 } 24 catch(FileNotFoundException notFoundExp) 25 { 26 Console.WriteLine("File: {0} not found!", notFoundExp.FileName); 27 } 28 finally 29 { 30 Console.WriteLine("Good Bye!"); 31 } 32 } 33 } 34 } سواءً كان الملف myFile.txt موجودًا أم غير موجود، أو كان يحتوي على بيانات أم فارغاً، ستظهر العبارة !Good Bye على الشاشة. ملاحظة: من الأفضل دومًا أن تحاول عدم استخدام عبارة try-catch وأن تستخدم عبارة if لاختبار الحالات التي تواجهك قبل تنفيذها. استخدم try-catch إذا كان ذلك ضروريًّا. والسبب في ذلك أنّ عمليّة معالجة الأخطاء بشكل عام تتطلّب المزيد من الموارد المخصّصة للبرنامج، مما قد يؤثّر على أداء البرنامج في حال تمّ استخدامها بشكل غير مدروس. تمارين داعمة تمرين 1 عدّل البرنامج Lesson16_02 بحيث يمكن الاستغناء عن عبارة try-catch تمامًا. (تلميح: ستحتاج إلى استخدام عبارتيّ if في هذه الحالة). تمرين 2 لتكن لدينا الشيفرة التالية: string input = Console.ReadLine(); int t = int.Parse(input); تطلب الشيفرة السابقة من المستخدم أن يدخل عددًا على شكل نص لتعمل على تحويله إلى قيمة عدديّة باستخدام التابع int.Parse. المطلوب هو إضافة عبارة try-catch إلى الشيفرة السابقة لمعالجة استثناء ممكن الحدوث في حال أدخل المستخدم قيمة مثل "u" وهي لا يمكن تحويلها إلى قيمة عدديّة كما هو واضح. (تلميح: استخدام الاستثناء FormatException الذي يُعبّر عن هذه الحالة). الخلاصة تعرّفنا في هذا الدرس على كيفيّة معالجة الأخطاء التي تظهر أثناء تنفيذ البرنامج. في الحقيقة هذا الموضوع ذو شجون! وهناك الكثير ليقال، ولكن يكفي الآن أن تتعرّف على المبادئ الأساسيّة لالتقاط الاستثناءات، وكيفيّة معالجتها. يمكنك استخدام هذا الأسلوب في جميع التطبيقات التي تُنشئها باستخدام سي شارب، مثل تطبيقات الويب بأنواعها، وتطبيقات سطح المكتب، وحتى تطبيقات الأجهزة الذكيّة باستخدام تقنيّة Xamarin.
  23. التجارة الإلكترونية E-commerce هي نوع معاصر من الأعمال يتضمّن تعاملات ماليّة وعمليّات بيع وشراء المنتجات من خلال الإنترنت. في الحقيقة أنّ مفهوم التجارة الإلكترونيّة ليس وليد هذه الأيّام. فقد بدأ هذا المفهوم بالظهور في مطلع الستينيَّات من القرن الماضي من خلال نظامين إلكترونيّين وهما Value-added network أو اختصارًا VAN وElectronic Dataa Interchange أو اختصارًا EDI. لا علاقة لكلٍّ من النظامين السابقين بالإنترنت، حيث يعتمد كلّ منهما بشكل أساسيّ، على توفير وسائل اتصال إلكترونيّة بين الشركات المختلفة التي غالبًا ما كانت شركات كبيرة، وذلك لتبادل الوثائق التجاريّة بأنواعها المختلفة (طلبات البيع – الفواتير – بيانات الشحن ...الخ). إلّا أنّه ومع ظهور الإنترنت تبلور مفهوم التجارة الإلكترونيّة بشكل كبير، وأصبح متاحًا للجميع، بدءًا من الشركات الكبيرة، وحتى الأفراد العاديين. حيث من الممكن بيع وشراء أيّ شيء بدءًا من المنتجات الماديّة الملموسة وانتهاءً بالمنتجات غير الملموسة مثل البرمجيّات والخدمات والاستشارات وغيرها. ماهو ماجنتو Magento؟ هو تطبيق برمجيّ مفتوح المصدر مخصّص لكي يكون منصّة متكاملة لإنجاز المهام الخاصّة بإجراء عمليّات البيع الإلكترونيّة بشكل متكامل من البداية إلى النهاية. ظهر ماجنتو عام 2008، ويمكنك باستخدامه أن تضيف المنتجات التي ترغب ببيعها على الإنترنت بشكل سلس، مما يسمح للزبون باستعراض هذه المنتجات بطريقة جميلة وأنيقة. بمجرّد اختيار الزبون لأيّ منتج ورغبته بشرائه، سيعمل ماجنتو على إدارة كافّة المهام اللازمة لإتمام عمليّة البيع بما فيها إنشاء طلب الشراء، وإتاحة إمكانية الدفع للزبون بأساليب متنوّعة، والسماح له بإضافة العناوين الخاصّة بالشحن، وإتمام عمليّة البيع. ما هي الأسباب التي قد تدفعني لاستخدام ماجنتو دون سواه هناك العديد من الأسباب التي تشجّع على ذلك. من أهم هذه الأسباب هي: ماجنتو كما أسلفنا هو مفتوح المصدر، ويمتلك إصدارًا مجّانيًا. بما أنّ ماجنتو مفتوح المصدر، فهو يمتلك طيفًا واسعًا من المطوّرين حول العالم الذين يعملون على إغناءه، وحلّ المشاكل التي قد تظهر بزمن قياسيّ. كما يمتلك دعمًا فنيًّا اجتماعيًّا، بمعنى أنّ أيّ شيء قد يصادفك يمكنك البحث عنه في الإنترنت وستجد له في الغالب جوابًا مقنعًا. بُني ماجنتو أساسًا ليكون تطبيقًا مخصّصًا للتجارة الإلكترونية، وهو يركّز عليها بشكل كامل، فهو يختلف من حيث المبدأ عن تطبيقات التجارة الإلكترونية مثل WooCommerce الذي يأتي كإضافة لـ WordPress، أو Drupal Commerce الذي يأتي إضافة لـ Drupal. يعطي هذا ميزة لماجنتو عليهما. من السهل ربط ماجنتو مع بوابات دفع من طرف ثالث third-party، أو مع خدمات شحن وتعقّب tracking خارجيّة. صُمّم ماجنتو بعناية ليكون متوافقًا بشكل ممتاز مع سيو SEO. يدعم ماجنتو العمل عن طريق الأجهزة الذكيّة على نحو جيّد، بحيث يتوافق مع القياسات المختلفة للشاشات، ويسمح باستخدام جميع المزايا التي نتعامل بها مع الأجهزة الذكية، مثل السحب والإفلات والتكبير والتصغير للصور، وغيرها الكثير. إصدارات ماجنتو الإصدار الحالي لماجنتو هو 2.1 ويأتي بعدّة أشكال، من أهمّها: الإصدار Magento Community وهو إصدار مجّاني يمكنك تحميله من الموقع الرسمي لماجنتو ومن ثمّ تنصيبه على خادوم مخصّص. الإصدار Magento Enterprise وهو الإصدار الخاص بالمؤسّسات، وبالأعمال الكبيرة وهو غير مجّاني بالطبع. الإصدار Magento Cloud ويشبه الإصدار Magento Enterprise من حيث المزايا، ولكن يتميّز عنه بأنّه مُستضاف ضمن الخدمة السحابيّة الخاصّة بشركة ماجنتو، وهو غير مجّاني أيضًا. المزايا التي يوفّرها ماجنتو الميّزة الأهم لماجنتو أنّه مفتوح المصدر open source. فيما عدا ذلك يمتلك هذا التطبيق في الواقع العديد من المزايا التي تجعله بحق من ألمع منصّات التجارة الإلكترونيّة المتوفّرة في الوقت الحالي. من أهم مزايا ماجنتو: يدعم ماجنتو العديد من اللغات العالميّة ومنها العربيّة. يوفّر ماجنتو العديد من طرق الدفع مثل PayPal و Google Checkouts وبطاقات الاعتماد Credit Cards وحتى الشيكات المصرفيّة العاديّة. يسمح بشحن المنتجات ضمن الطلب الواحد إلى عناوين شحن مختلفة. يجعل من السهل إضافة وتصفّح المنتجات، كما يسمح للمستخدم بتصفيتها وفق معايير محدّدة، ويسمح أيضًا بعرضها بطريقة القائمة أو بطريقة الشبكة. يدعم عملات متعدّدة بالإضافة إلى إمكانيّة تحديد واحتساب الضرائب على السلع المباعة. صمّم ماجنتو ليكون متكيّفًا مع محرّكات البحث (SEO). يدعم ماجنتو واجهات متوافقة مع مختلف أنواع الشاشات بما فيها شاشات الأجهزة اللوحيّة والأجهزة الذكيّة. يسمح ماجنتو للمطوّرين بتوسعته. فمن الممكن للمطوّرين أن يطوّروا إضافات له تقوم ببعض الأعمال الإضافيّة التي لا يدعمها ماجنتو أصلًا وتحتاجها بعض الشركات. ماذا سنتعلّم في هذه السلسلة سنتناول في هذه السلسلة معلومات غنيّة عن ماجنتو، ويتضمّن ذلك كيفيّة التعامل مع مختلف الجوانب في Magento بدءًا من عمليّة تنصيبه، وتجهيز المنتجات وتصنيفاتها، وإعداد المخزون والضرائب والشحن. كما سنتعلّم طريقة إعداد وسائل الدفع المختلفة، والتعامل مع العملات المختلفة، وآلية سير الطلبيّات وإدارتها وغيرها من المهام الأساسيّة في عالم التجارة الإلكترونيّة. الخلاصة ماجنتو تطبيق مفتوح المصدر، وهو واحد من الحلول المهمّة في عالم التجارة الإلكترونيّة، وهو مفيد للشركات الكبيرة والمتوسّطة والصغيرة وحتى الأفراد العاديين وذلك بحسب أنواع الإصدارات المتاحة له. يُعتبر ماجنتو مرنًا وقابلًا للتوسعة. إذا كنت مبرمجًا ومهتمًّا في هذا المجال يمكنك بناء مُلحقات/إضافات extensions لماجنتو تعمل على التكامل معه، وتوفير حلول مخصّصة للشركات المختلفة. سنعمل في الدرس التالي على تنصيب ماجنتو، كما سنتحدّث لاحقًا عن الخيارات المتاحة في حال لم نرغب بتنصيب ماجنتو على خادوم مخصّص.
  24. أنصحك بإطار عمل Lavarel فهي تدعم MVC وأيضًا يمكنك استخدام تقنيّة DI معها، وهي تدعم أيضًا الـ Authorization (انظر هنا). كما أنّها تمتلك بيئة تطوير تجريبيّة توفّر لك جميع الأدوات التطويريّة اللازمة للبدء بالبرمجة من خلال Virtual Machine مُعدّ لهذا الغرض (انظر هنا) توجد مصادر كثيرة للتعلّم ولكنّ المشكلة في أنّه ينبغي عليك أن تكون ملمًّا أصلًا في PHP كي تبدأ بـ Lavarel. انظر المصادر المجّانيّة التالية للتعلّم: http://learninglaravel.net/ https://laracasts.com/series https://laracasts.com/series/laravel-5-fundamentals https://belitsoft.com/laravel-development-services/laravel-5-tutorial
×
×
  • أضف...