بناء واجهات تطبيقات Xamarin باستخدام XAML


حسام برهان

يُعتبر هذا الدرس من الدروس المهمّة في سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms. في الواقع لقد أشرنا إلى هذا الدرس عدّة مرّات خلال الدروس السابقة. تكمن المشكلة في التطبيقات التي تناولناها في الدروس السابقة إلى أنّنا كنّا ننشئ واجهة التطبيق باستخدام الشيفرة البرمجيّة بشكل كامل مما يؤدّي إلى مشكلة كبيرة في تنظيم البرامج لأنّنا في هذه الحالة سنضطر غالبًا إلى وضع الشيفرة المسؤولة عن بناء الواجهات مع الشيفرة البرمجيّة المسؤولة عن تمثيل منطق العمل business logic في البرنامج، وهذا أمر سيء بالطبع.

main.png

وفّرت Xamarin حلًا ممتازًا لهذه المشكلة يجعل من البرمجة باستخدامها تجربة غنيّة ومعاصرة، وذلك من خلال إنشاء الواجهات باستخدام رماز XAML. يشبه هذا الرماز إلى حدٍّ كبير رماز XML. حيث يتمّ إنشاء العناصر المرئيّة وضبط خصائصها باستخدام ما يشبه عناصر وسمات XML. تكمن الفائدة الأساسيّة من استخدام هذا الأسلوب هو عزل بناء واجهات البرنامج عن الشيفرة البرمجيّة المسؤولة عن معالجة منطق البرنامج، وهذا أمر مهم وخصوصًا عند بناء تطبيقات كبيرة. لنقارن مثلًا بين إنشاء لصيقة باستخدام الشيفرة البرمجيّة وباستخدام XAML:

fig01.png

لا أعتقد أنّنا سنبذل كثيرًا من الجهد لملاحظة أنّ أسلوب 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 كما في الشكل التالي:

fig02.png

انقر بعد ذلك الزر 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 بعد إضافة ملف المحتوى:

fig03.png

الملف 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 لتحصل على الوقت الحالي ضمن اللصيقة. انظر إلى الشكل التالي:

fig04.png

هناك أمران ملفتان في الشيفرة السابقة:

  • أسندنا قيمة الوقت الحالي إلى الخاصيّة 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. انظر إلى الشكل التالي:

fig05.png

انقره نقرًا مزدوجًا لترى محتوياته. ستجد الصنف 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 الذي يحتوي على واجهة التطبيق.

 

Quote

أيُّ عنصر يحتوي على عناصر أخرى ينبغي أن يكون له وسم فتح ووسم إغلاق، بحيث تُوضع العناصر المطلوبة بين هذين الوسمين مثل:


<StackLayout>
	-Other elements-
</ StackLayout >

أمّا بالنسبة للعناصر التي لا تحتوي عناصر أخرى، فمن الممكن أن نكتفي بوسم فتح فقط ونغلقه باستخدام الرمزين /> مثل:


<Label Text = "Hello" />

 


الخلاصة

تعلّمنا في هذه الدرس المبادئ الأساسيّة لبناء الواجهات باستخدام رماز XAML بدلًا عن أسلوب الشيفرة التي اعتمدناه في الدروس السابقة. ستلمس الفائدة العظيمة لبناء الواجهات بهذا الأسلوب عندما تبدأ ببناء تطبيقات واقعيّة، حيث سيجعل هذا الأسلوب من عمليّة بناء التطبيق أمرًا يسيرًا وواضحًا من خلال فصل الواجهة بما تحتويه من عناصر مرئيّة عن الشيفرة البرمجيّة المخصّصة للتفاعل مع هذه الواجهة، وذلك ضمن ملفّين منفصلين. تساعد هذه العمليّة على ترتيب البرامج وجعلها أسهل للفهم وللصيانة وللتطوير المستقبلي، وخصوصًا عندما تصبح البرامج أكبر ومعقّدة أكثر. هنالك الكثير مما يمكن قوله حول هذه التقنيّة سنتناول ذلك تباعًا من خلال الدروس القادمة. سنبني في الدرس القادم تطبيق عملي رياضيّاتيّ مفيد يتمثّل في حل معادلة من الدرجة الثانيّة حيث سنبني واجهة التطبيق من خلال XAML.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن